Spring?question問(wèn)題小結(jié)
Spring
一. 控制反轉(zhuǎn)(IoC)
1.手動(dòng)
使用了Spring的 @Configuration
和 @Bean
注解來(lái)明確指定了哪些類需要被納入容器的管理。在 AppConfig
配置類中,通過(guò) @Bean
注解創(chuàng)建了 Service
和 Controller
的實(shí)例,Spring會(huì)自動(dòng)將這些實(shí)例納入容器的管理,并處理它們之間的依賴關(guān)系。
// 定義一個(gè)Service接口 public interface Service { void doSomething(); } // 實(shí)現(xiàn)Service接口的具體類 public class ServiceImpl implements Service { @Override public void doSomething() { System.out.println("Service is doing something."); } } // 定義一個(gè)Controller類,它依賴于Service接口 public class Controller {d private Service service; // 通過(guò)構(gòu)造函數(shù)注入依賴 public Controller(Service service) { this.service = service; } public void doAction() { service.doSomething(); } } // 在應(yīng)用的入口處,使用Spring容器創(chuàng)建實(shí)例并進(jìn)行依賴關(guān)系的管理 public class Main { public static void main(String[] args) { // 創(chuàng)建Spring容器 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 從容器中獲取Controller實(shí)例 Controller controller = context.getBean(Controller.class); // 調(diào)用Controller的方法,會(huì)自動(dòng)調(diào)用Service的方法 controller.doAction(); } } // 定義一個(gè)配置類,用于告訴Spring容器如何創(chuàng)建和管理Bean @Configuration public class AppConfig { @Bean public Service service() { return new ServiceImpl(); } @Bean public Controller controller(Service service) { return new Controller(service); } }
2.自動(dòng)
SpringApplication.run(CommunityApplication.class, args)
會(huì)掃描應(yīng)用中的所有組件(包括被 @Component
、 @Service
、 @Repository
等注解標(biāo)記的類),并將它們納入Spring容器的管理。通過(guò)調(diào)用 SpringApplication.run
方法啟動(dòng)應(yīng)用,會(huì)自動(dòng)創(chuàng)建一個(gè)Spring應(yīng)用上下文(ApplicationContext),并初始化整個(gè)應(yīng)用的配置和組件。
public static void main(String[] args) { SpringApplication.run(CommunityApplication.class, args); }
@SpringBootApplication
實(shí)際上是一個(gè)組合注解,包含了 @SpringBootConfiguration
、 @EnableAutoConfiguration
和 @ComponentScan
三個(gè)注解。其中, @SpringBootConfiguration
注解表示這是一個(gè)Spring Boot的配置類, @EnableAutoConfiguration
注解啟用自動(dòng)配置, @ComponentScan
注解指定要掃描的包路徑。使用Spring Boot的自動(dòng)化機(jī)制可以方便快捷地掃描和管理所有的Bean,而手動(dòng)進(jìn)行依賴注入可以更加精確地控制容器中的Bean。
Q1:使用@Repository注解時(shí),怎么在Spring的配置文件中進(jìn)行相關(guān)的配置?
1、Spring
在使用@Repository注解時(shí),可以在Spring的配置文件中進(jìn)行相關(guān)的配置。以下是配置步驟:
1.在Spring的配置文件中添加 context:component-scan
標(biāo)簽,用于開(kāi)啟組件掃描功能,掃描帶有 @Repository
注解的類。示例如下:
<context:component-scan base-package="com.example.repository" />
其中, base-package
屬性指定了要掃描的包路徑。
2.如果需要配置數(shù)據(jù)源和事務(wù)管理器,可以在Spring的配置文件中進(jìn)行相關(guān)的配置。示例如下:
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <!-- 數(shù)據(jù)源相關(guān)配置 --> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
其中, dataSource
是數(shù)據(jù)源的配置, transactionManager
是事務(wù)管理器的配置。
3.如果需要使用 @Repository
注解對(duì)應(yīng)的數(shù)據(jù)訪問(wèn)異常轉(zhuǎn)化功能,可以配置相關(guān)的異常轉(zhuǎn)化器。示例如下:
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
這個(gè)配置會(huì)自動(dòng)為帶有 @Repository
注解的類添加異常轉(zhuǎn)化功能。通過(guò)以上配置,就可以在Spring的配置文件中配置 @Repository
注解相關(guān)的配置。注意,具體的配置內(nèi)容根據(jù)實(shí)際需求和使用的技術(shù)選擇可能會(huì)有所不同。
2、Spring Boot
在使用Spring Boot時(shí),可以省略繁瑣的配置過(guò)程,因?yàn)镾pring Boot提供了自動(dòng)配置的功能。對(duì)于@Repository注解的配置,可以按照以下步驟進(jìn)行:
- 在Spring Boot主類上添加
@SpringBootApplication
注解,該注解包含了@EnableAutoConfiguration
注解,用于開(kāi)啟自動(dòng)配置功能。 - 確保@Repository注解所在的包或子包在主類的掃描范圍內(nèi),可以通過(guò)
@ComponentScan
注解進(jìn)行配置,或者將主類放在包的頂層位置。 - 如果需要配置數(shù)據(jù)源,可以在
application.properties
或application.yml
配置文件中添加相關(guān)的配置項(xiàng),如下所示:
propertiesCopy codespring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver
這樣,Spring Boot會(huì)根據(jù)配置文件的內(nèi)容自動(dòng)配置數(shù)據(jù)源。
4.如果需要事務(wù)管理器,可以在需要使用事務(wù)的類或方法上添加 @Transactional
注解,Spring Boot會(huì)自動(dòng)配置事務(wù)管理器。 通過(guò)以上步驟,就可以在Spring Boot中使用@Repository注解,無(wú)需進(jìn)行繁瑣的配置,Spring Boot會(huì)根據(jù)約定和自動(dòng)配置的機(jī)制自動(dòng)完成相關(guān)的配置。 Q2:@Primary有什么用
@Primary注解是Spring框架中的一個(gè)注解,用于標(biāo)識(shí)主要的Bean實(shí)例。當(dāng)存在多個(gè)同類型的Bean實(shí)例時(shí),通過(guò)使用@Primary注解,可以指定其中一個(gè)Bean實(shí)例為首選的主要實(shí)例。
Q3:@Repository有什么用
@Repository注解是Spring框架中的一個(gè)注解,用于標(biāo)識(shí)持久層組件(如DAO類)。
@Repository的作用主要有以下幾點(diǎn):
- 標(biāo)識(shí)持久層組件:通過(guò)在持久層的類上添加
@Repository
注解,可以告訴Spring容器該類是一個(gè)用于數(shù)據(jù)訪問(wèn)的組件。Spring在進(jìn)行組件掃描時(shí),會(huì)掃描帶有@Repository
注解的類,并將其實(shí)例化為Bean
。 - 提供異常轉(zhuǎn)化功能:
@Repository
注解還提供了數(shù)據(jù)訪問(wèn)異常的轉(zhuǎn)化機(jī)制。當(dāng)數(shù)據(jù)訪問(wèn)過(guò)程中發(fā)生異常時(shí),Spring會(huì)將底層的數(shù)據(jù)訪問(wèn)異常(如JDBC異常)轉(zhuǎn)化為Spring的統(tǒng)一異常體系,使得應(yīng)用程序可以更方便地處理和捕獲異常。 - 具備@Component的功能:
@Repository
注解是@Component
注解的派生注解,因此@Repository
注解具備@Component
的所有功能。它可以被Spring容器自動(dòng)掃描并裝配,可以使用@Autowired
或@Resource
等注解進(jìn)行依賴注入,也可以使用@Qualifier注解進(jìn)行指定具體的實(shí)現(xiàn)類。
Q4:談?wù)剬?duì)IoC的理解
控制:創(chuàng)建、實(shí)例化對(duì)象的權(quán)力反轉(zhuǎn):將這些權(quán)力交給IoC容器和Spring框架
將對(duì)象之間的依賴關(guān)系交給IoC容器管理,由IoC容器完成對(duì)象的注入,簡(jiǎn)化開(kāi)發(fā),IoC容器像是一個(gè)工廠,創(chuàng)建一個(gè)對(duì)象時(shí)只配置好,不需要考慮怎么被創(chuàng)建出來(lái)的。Spring中用Xml文件配置bean,在Spring Boot中用注解配置。
Q5:@Component和@Bean的區(qū)別
- @Component注解作用于類,而@Bean注解作用于方法
- @Component通常是通過(guò)類路徑掃描來(lái)自動(dòng)偵察以及自動(dòng)裝配到Spring容器中;
- @Bean在標(biāo)有該注解的方法中定義產(chǎn)生這個(gè)bean,@Bean告訴了Spring這是某個(gè)類的實(shí)例,當(dāng)我需要的時(shí)候還給我。
- @Bean比@Component的自定義性更強(qiáng)
Q6:注入Bean的注解
- @Autowired
- @Resource
- @Inject
1.@Autowired
屬于 Spring
內(nèi)置的注解,默認(rèn)的注入方式是 byType(根據(jù)類型進(jìn)行匹配)
,首先根據(jù)接口類型去匹配并注入 Bean
。如果一個(gè)接口有多個(gè)實(shí)現(xiàn)類,注入方式就會(huì)變?yōu)?byName(根據(jù)名稱匹配)
舉個(gè)例子, SmsService
接口有兩個(gè)實(shí)現(xiàn)類: SmsServiceImpl1
和 SmsServiceImpl2
,且它們都已經(jīng)被 Spring
容器所管理。
// 報(bào)錯(cuò),byName 和 byType 都無(wú)法匹配到 bean @Autowired private SmsService smsService; // 正確注入 SmsServiceImpl1 對(duì)象對(duì)應(yīng)的 bean @Autowired private SmsService smsServiceImpl1; // 正確注入 SmsServiceImpl1 對(duì)象對(duì)應(yīng)的 bean // smsServiceImpl1 就是我們上面所說(shuō)的名稱 @Autowired @Qualifier(value = "smsServiceImpl1") private SmsService smsService;
2.@Resource
屬于 JDK 提供的注解,默認(rèn)注入方式為 byName
。如果無(wú)法通過(guò)名稱匹配到對(duì)應(yīng)的 Bean 的話,注入方式會(huì)變?yōu)?byType
。 @Resource
有兩個(gè)比較重要且日常開(kāi)發(fā)常用的屬性: name
(名稱)、 type
(類型)。
public @interface Resource { String name() default ""; Class<?> type() default Object.class; }
如果僅指定 name
屬性則注入方式為 byName
,如果僅指定 type
屬性則注入方式為 byType
,如果同時(shí)指定 name
和 type
屬性(不建議這么做)則注入方式為 byType
+ byName
。
// 報(bào)錯(cuò),byName 和 byType 都無(wú)法匹配到 bean @Resource private SmsService smsService; // 正確注入 SmsServiceImpl1 對(duì)象對(duì)應(yīng)的 bean @Resource private SmsService smsServiceImpl1; // 正確注入 SmsServiceImpl1 對(duì)象對(duì)應(yīng)的 bean(比較推薦這種方式) @Resource(name = "smsServiceImpl1") private SmsService smsService;
總結(jié)一下:
@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。Autowired
默認(rèn)的注入方式為byType
(根據(jù)類型進(jìn)行匹配),@Resource
默認(rèn)注入方式為byName
(根據(jù)名稱進(jìn)行匹配)。- 當(dāng)一個(gè)接口存在多個(gè)實(shí)現(xiàn)類的情況下,
@Autowired
和@Resource
都需要通過(guò)名稱才能正確匹配到對(duì)應(yīng)的 Bean。Autowired
可以通過(guò)@Qualifier
注解來(lái)顯式指定名稱,@Resource
可以通過(guò)name
屬性來(lái)顯式指定名稱。@Autowired
支持在構(gòu)造函數(shù)、方法、字段和參數(shù)上使用。@Resource
主要用于字段和方法上的注入,不支持在構(gòu)造函數(shù)或參數(shù)上使用
Q8:Bean的作用域
- Singleton:IoC容器中只有唯一的bean實(shí)例,Spring的bean默認(rèn)都是單例的。在之后的每次請(qǐng)求或注入時(shí),都會(huì)返回同一個(gè)實(shí)例,而不會(huì)重新創(chuàng)建新的實(shí)例。
- prototype:每次獲取都會(huì)創(chuàng)建一個(gè)新的 bean 實(shí)例。與默認(rèn)的單例模式不同,每次通過(guò)容器獲取prototype作用域的Bean時(shí),都會(huì)創(chuàng)建一個(gè)新的實(shí)例。原型模式的Bean不適合進(jìn)行依賴注入,因?yàn)槊看巫⑷攵紩?huì)得到一個(gè)新的實(shí)例,無(wú)法保證依賴關(guān)系的一致性。
- request (僅 Web 應(yīng)用可用): 每一次 HTTP 請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的 bean(請(qǐng)求 bean),該 bean 僅在當(dāng)前 HTTP request 內(nèi)有效。
- session (僅 Web 應(yīng)用可用) : 每一次來(lái)自新 session 的 HTTP 請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的 bean(會(huì)話 bean),該 bean 僅在當(dāng)前 HTTP session 內(nèi)有效。
Q9:為什么在Spring boot沒(méi)有配置 bean 的作用域
在Spring Boot中,為了簡(jiǎn)化配置和提高開(kāi)發(fā)效率,通常不需要顯式地配置Bean的作用域。Spring Boot默認(rèn)使用單例模式來(lái)管理Bean,即每個(gè)Bean在容器中只會(huì)存在一個(gè)實(shí)例。 這是因?yàn)镾pring Boot遵循"約定優(yōu)于配置"的原則,通過(guò)自動(dòng)配置和默認(rèn)配置來(lái)簡(jiǎn)化開(kāi)發(fā)過(guò)程。在大多數(shù)情況下,單例模式已經(jīng)能夠滿足開(kāi)發(fā)需求,因此默認(rèn)使用單例模式可以減少不必要的配置。 如果需要使用其他作用域,如原型(prototype)、會(huì)話(session)、請(qǐng)求(request)等,可以在需要的地方使用特定的注解來(lái)標(biāo)記,而不需要在配置文件中顯式配置。例如,可以使用** @Scope("prototype")
**注解來(lái)將特定的Bean定義為原型模式。
二. 面向切面編程(AOP)
術(shù)語(yǔ) | 含義 |
---|---|
目標(biāo)(Target) | 目標(biāo)對(duì)象/被通知的對(duì)象 |
代理(Proxy) | 向目標(biāo)對(duì)象應(yīng)用通知之后創(chuàng)建的代理對(duì)象 |
連接點(diǎn)(JoinPoint) | 目標(biāo)對(duì)象的所屬類中,定義的所有方法均為連接點(diǎn) |
切入點(diǎn)(Pointcut) | 被切面攔截/增強(qiáng)的連接點(diǎn)(切入點(diǎn)一定是連接點(diǎn),連接點(diǎn)不一定是織入點(diǎn)) |
通知(Advice) | 增強(qiáng)的邏輯/代碼,也是攔截到目標(biāo)對(duì)象的連接點(diǎn)后應(yīng)該做的事 |
織入(Weaving) | 將通知 應(yīng)用到目標(biāo)對(duì)象,產(chǎn)生代理對(duì)象的過(guò)程動(dòng)作 |
方面主鍵(Aspect) | Pointcut+Advice |
Q1:談?wù)剬?duì)AOP的理解
能夠?qū)⑴c業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯和責(zé)任所封裝起來(lái),例如:事務(wù)處理、權(quán)限控制,日志管理,減少重復(fù)代碼,比較好維護(hù)。
Spring AOP
基于動(dòng)態(tài)代理的,如果代理的對(duì)象實(shí)現(xiàn)了某個(gè)接口,那么 Spring AOP
會(huì)使用 JDK Proxy
,去創(chuàng)建代理對(duì)象;而對(duì)于沒(méi)有實(shí)現(xiàn)接口的對(duì)象, Spring AOP
使用 Cglib
生成一個(gè)被代理對(duì)象的子類作為代理。
Spring AOP
的工作原理:自動(dòng)為目標(biāo)對(duì)象生成代理,并在方法調(diào)用時(shí)織入切面邏輯。
代理對(duì)象是通過(guò) Spring AOP
自動(dòng)生成的,并且已經(jīng)被注入到 ApplicationContext
中。具體來(lái)說(shuō),代理對(duì)象是在以下代碼中獲取的:
UserService userService = context.getBean(UserService.class);
// 主類 @SpringBootApplication public class Application { public static void main(String[] args) { ApplicationContext context = SpringApplication.run(Application.class, args); UserService userService = context.getBean(UserService.class); userService.addUser("john", "123456"); } }
在這個(gè)示例中,通過(guò) ApplicationContext
的 getBean
方法來(lái)獲取代理對(duì)象。根據(jù) Spring AOP
的配置,如果 UserService
接口被代理,那么獲取到的 userService
對(duì)象就是代理對(duì)象;如果 UserServiceImpl
類被代理,那么獲取到的 userService
對(duì)象也是代理對(duì)象。
Q2:為什么要對(duì)Spring AOP進(jìn)行代理,不代理會(huì)怎樣?
在 Spring AOP
中,代理是實(shí)現(xiàn)切面功能的關(guān)鍵。如果不進(jìn)行代理,切面邏輯將無(wú)法被織入到目標(biāo)對(duì)象的方法調(diào)用中,失去了 AOP
的作用。 具體來(lái)說(shuō),代理對(duì)象在目標(biāo)對(duì)象和調(diào)用方之間充當(dāng)了一個(gè)中間層。當(dāng)調(diào)用方調(diào)用代理對(duì)象的方法時(shí),代理對(duì)象會(huì)在方法執(zhí)行前后執(zhí)行切面邏輯。這樣,我們可以在切面中添加一些額外的邏輯,比如日志記錄、事務(wù)管理、性能監(jiān)控等。代理對(duì)象將切面邏輯織入到目標(biāo)對(duì)象的方法調(diào)用中,從而實(shí)現(xiàn)了橫切關(guān)注點(diǎn)的模塊化。 如果不進(jìn)行代理,切面邏輯將無(wú)法被自動(dòng)應(yīng)用到目標(biāo)對(duì)象的方法調(diào)用中。這意味著我們需要在每個(gè)目標(biāo)對(duì)象方法的調(diào)用處手動(dòng)添加切面邏輯,這樣會(huì)導(dǎo)致代碼的重復(fù)和冗余,不利于代碼的維護(hù)和擴(kuò)展。而代理機(jī)制可以自動(dòng)為目標(biāo)對(duì)象生成代理,并在方法調(diào)用時(shí)織入切面邏輯,使得切面的應(yīng)用更加便捷和靈活。 除了將切面邏輯織入到方法調(diào)用中,代理還可以實(shí)現(xiàn)其他功能,比如延遲加載、事務(wù)管理、緩存等。代理對(duì)象可以攔截方法調(diào)用,根據(jù)需要進(jìn)行一些額外的處理,從而提供更多的功能。
Q3:那在以下代碼中,誰(shuí)是目標(biāo)對(duì)象,誰(shuí)是代理對(duì)象,誰(shuí)是調(diào)用方?
public class AlphaAspect { @Pointcut("execution(* com.newcoder.community.service.*.*(..))")//所有類、所有方法 public void pointcut(){} @Before("pointcut()")//連接點(diǎn)開(kāi)始記日志 public void before(){ System.out.println("before"); } @After("pointcut()")//連接點(diǎn)后記日志 public void after(){ System.out.println("after"); } @AfterReturning("pointcut()")//有返回值后記日志 public void afterReturn(){ System.out.println("afterReturn"); } @AfterThrowing("pointcut()")//拋異常后記日志 public void afterThrowing(){ System.out.println("afterThrowing"); } @Around("pointcut()")//前后都織入 執(zhí)行代理對(duì)象,織入代理對(duì)象,用來(lái)代替原始對(duì)象 public Object around(ProceedingJoinPoint joinPoint) throws Throwable{//ProceedingJoinPoint為連接點(diǎn),織入部位 System.out.println("around before"); Object obj = joinPoint.proceed();//proceed調(diào)目標(biāo)主鍵 調(diào)用原始對(duì)象方法 System.out.println("around after"); return obj; }
在這個(gè)例子中,目標(biāo)對(duì)象是com.newcoder.community.service包下的所有類的所有方法。 AlphaAspect類本身是一個(gè)切面類,其中包含了各種注解標(biāo)記的通知方法。這些通知方法會(huì)在目標(biāo)對(duì)象的方法調(diào)用前后執(zhí)行相應(yīng)的邏輯。因此,AlphaAspect類是代理對(duì)象。 調(diào)用方是在業(yè)務(wù)代碼中調(diào)用目標(biāo)對(duì)象的方法的地方,這個(gè)例子中沒(méi)有給出具體的業(yè)務(wù)代碼,所以無(wú)法確定調(diào)用方是什么。
所以,代理對(duì)象會(huì)在目標(biāo)對(duì)象的方法調(diào)用前后執(zhí)行相應(yīng)的邏輯。
Q4:那為什么說(shuō)向目標(biāo)對(duì)象應(yīng)用通知之后創(chuàng)建代理對(duì)象?
在AOP中,通知(Advice)是切面的核心邏輯,用于在目標(biāo)對(duì)象的方法調(diào)用前后執(zhí)行特定的操作。為了將通知應(yīng)用到目標(biāo)對(duì)象的方法調(diào)用中,需要?jiǎng)?chuàng)建代理對(duì)象來(lái)包裝目標(biāo)對(duì)象。代理對(duì)象負(fù)責(zé)攔截目標(biāo)對(duì)象的方法調(diào)用,并在適當(dāng)?shù)臅r(shí)機(jī)執(zhí)行通知。 代理對(duì)象的創(chuàng)建過(guò)程可以分為兩種方式:靜態(tài)代理和動(dòng)態(tài)代理。
- 靜態(tài)代理:在編譯時(shí)就已經(jīng)將切面邏輯與目標(biāo)對(duì)象織入到了一起,生成了代理類的字節(jié)碼文件。代理對(duì)象是在編譯時(shí)生成的,不需要在運(yùn)行時(shí)再去創(chuàng)建。
- (AspectJ)動(dòng)態(tài)代理:在運(yùn)行時(shí)動(dòng)態(tài)地生成代理對(duì)象。動(dòng)態(tài)代理需要借助于Java的反射機(jī)制來(lái)實(shí)現(xiàn)。通過(guò)動(dòng)態(tài)創(chuàng)建代理對(duì)象,可以根據(jù)切面的配置在運(yùn)行時(shí)將通知應(yīng)用到目標(biāo)對(duì)象的方法調(diào)用中。 在Spring AOP中,默認(rèn)使用的是動(dòng)態(tài)代理。Spring使用JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理兩種方式來(lái)創(chuàng)建代理對(duì)象。如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,則使用JDK動(dòng)態(tài)代理;如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)接口,則使用CGLIB動(dòng)態(tài)代理。 無(wú)論是靜態(tài)代理還是動(dòng)態(tài)代理,代理對(duì)象的創(chuàng)建都是在應(yīng)用通知之后進(jìn)行的。通過(guò)代理對(duì)象,切面邏輯可以被織入到目標(biāo)對(duì)象的方法調(diào)用中,實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的增強(qiáng)。
Q5:目標(biāo)對(duì)象所有的方法就是連接點(diǎn)嗎?
是的,目標(biāo)對(duì)象的所有方法都可以作為連接點(diǎn)。連接點(diǎn)是程序執(zhí)行過(guò)程中的特定點(diǎn),包括方法的調(diào)用、異常的拋出、字段的訪問(wèn)等。在AOP中,最常見(jiàn)的連接點(diǎn)是方法的調(diào)用,因?yàn)槲覀兺ǔOM诜椒ǖ膱?zhí)行前、執(zhí)行后或發(fā)生異常時(shí)插入切面邏輯。 在AOP中,可以選擇在目標(biāo)對(duì)象的所有方法上織入切面邏輯,也可以選擇只在特定的方法上織入切面邏輯,這取決于所定義的切點(diǎn)(Pointcut)。切點(diǎn)是定義在哪些連接點(diǎn)上織入切面邏輯的規(guī)則,可以使用表達(dá)式或注解來(lái)定義切點(diǎn)。
Q6:切入點(diǎn)是類路徑嗎?
不,切入點(diǎn)(Pointcut)并不是類路徑。切入點(diǎn)是在 AOP
中用于定義哪些連接點(diǎn)(JoinPoint)應(yīng)該被織入切面邏輯的規(guī)則。 切入點(diǎn)可以使用表達(dá)式或注解來(lái)定義。使用表達(dá)式定義切入點(diǎn)時(shí),可以使用 AspectJ
風(fēng)格的切入點(diǎn)表達(dá)式,如 execution、within、args
等關(guān)鍵字,以及類、方法、參數(shù)等信息來(lái)描述需要織入切面邏輯的連接點(diǎn)。使用注解來(lái)定義切入點(diǎn)時(shí),可以通過(guò)在目標(biāo)對(duì)象的方法上添加特定的注解,然后通過(guò)切入點(diǎn)表達(dá)式來(lái)匹配這些注解來(lái)確定要織入切面邏輯的連接點(diǎn)。 切入點(diǎn)可以非常靈活地定義,可以選擇在目標(biāo)對(duì)象的所有方法上織入切面邏輯,也可以選擇只在特定的方法或類上織入切面邏輯。切入點(diǎn)的定義是根據(jù)業(yè)務(wù)需求和設(shè)計(jì)目標(biāo)來(lái)確定的,通常會(huì)根據(jù)實(shí)際情況來(lái)選擇需要織入切面邏輯的連接點(diǎn)。
Q7:織入的過(guò)程是不是就相當(dāng)于動(dòng)態(tài)代理和靜態(tài)代理過(guò)程?
? 是的,織入的過(guò)程可以類比為動(dòng)態(tài)代理和靜態(tài)代理的過(guò)程。? 在靜態(tài)代理中,代理對(duì)象和目標(biāo)對(duì)象實(shí)現(xiàn)同一個(gè)接口,代理對(duì)象在調(diào)用目標(biāo)對(duì)象方法的前后插入額外的邏輯。這種方式需要在編譯時(shí)期就確定代理關(guān)系,并在代碼中顯式地指定代理對(duì)象。? 在動(dòng)態(tài)代理中,代理對(duì)象是在運(yùn)行時(shí)動(dòng)態(tài)生成的,無(wú)需事先編寫代理類。通過(guò)Java的反射機(jī)制,動(dòng)態(tài)代理可以在運(yùn)行時(shí)攔截并處理目標(biāo)對(duì)象的方法調(diào)用。在動(dòng)態(tài)代理中,我們可以在目標(biāo)對(duì)象方法的調(diào)用前后插入切面邏輯。? 類似地, AOP
的織入過(guò)程也是在運(yùn)行時(shí)動(dòng)態(tài)生成的。 AOP
框架會(huì)根據(jù)切入點(diǎn)的定義,對(duì)目標(biāo)對(duì)象的方法調(diào)用進(jìn)行攔截,并根據(jù)切面邏輯對(duì)其進(jìn)行增強(qiáng)。這個(gè)過(guò)程可以看作是在運(yùn)行時(shí)動(dòng)態(tài)代理的過(guò)程。 不同的是,動(dòng)態(tài)代理和靜態(tài)代理通常是針對(duì)單個(gè)類或?qū)ο筮M(jìn)行的,而** AOP
的織入可以同時(shí)作用于多個(gè)類和對(duì)象**,根據(jù)切入點(diǎn)的定義對(duì)滿足條件的連接點(diǎn)進(jìn)行增強(qiáng)。
三、Spring MVC
Q1:說(shuō)說(shuō)自己對(duì)Spring MVC的了解
MVC是模型(Model)、視圖(View)、控制器(Controller)的簡(jiǎn)寫,它是通過(guò)將業(yè)務(wù)邏輯、數(shù)據(jù)、顯示分離組織代碼。
Q2:Spring MVC的核心組件有哪些?
DispatcherServlet
:核心的中央處理器,負(fù)責(zé)接受請(qǐng)求、分發(fā),并給予客戶響應(yīng)HandlerMapping
:處理器映射器,根據(jù)URL去匹配查找能處理的Handler
,并會(huì)將請(qǐng)求涉及到的攔截器和Handler
一起封裝HandlerAdapter
:處理器適配器 ,根據(jù)HandlerMapping
找到的Handler
,適配執(zhí)行對(duì)應(yīng)的Handler
.Handler
:請(qǐng)求處理器,處理實(shí)際請(qǐng)求的處理器ViewResolver
:視圖解析器,根據(jù)Handler
返回的邏輯視圖,解析并渲染真正的視圖,并傳遞給DispatcherServlet響應(yīng)客戶端
Q3:Spring MVC的工作原理
- 客戶端發(fā)送請(qǐng)求,
DispatcherServlet
攔截請(qǐng)求DispatcherServlet
根據(jù)請(qǐng)求信息調(diào)用HandlerMapping
。 HandlerMapping
根據(jù)URL去匹配查找能處理的Handler
,并會(huì)將涉及到的攔截器和Handler
一起封裝DispatcherServlet
調(diào)用HandlerAdapter
適配器執(zhí)行Handler
Handler
完成對(duì)用戶的請(qǐng)求的處理,返回一個(gè)ModelAndView
對(duì)象給DispatcherServlet
- ViewResolver 根據(jù)邏輯
View
查找實(shí)際的View
DispatcherServlet
把返回的Model
傳給View
View
返回客戶端
Q4:Handler 是什么?
Handler 是指在 Spring MVC 框架中用于處理用戶請(qǐng)求的組件。Handler 可以是一個(gè) Controller 類中的方法、一個(gè) Servlet、一個(gè) WebSocket 處理器或其他可處理請(qǐng)求的組件。
Q5:DispatcherServlet 為什么調(diào)用 HandlerAdapter適配器執(zhí)行 Handler?
不同的 Handler 可能有不同的處理方式,例如一個(gè) Controller 方法、一個(gè) Servlet、一個(gè) WebSocket 處理器等。為了統(tǒng)一處理不同類型的 Handler,需要使用適配器模式將不同類型的 Handler 適配為統(tǒng)一的處理方式。 HandlerAdapter 適配器的作用就是根據(jù)不同類型的 Handler,將請(qǐng)求信息進(jìn)行適配,使其能夠被統(tǒng)一調(diào)用并執(zhí)行。它根據(jù) Handler 的類型,調(diào)用相應(yīng)的適配方法,將請(qǐng)求信息傳遞給 Handler 進(jìn)行處理,并獲取處理結(jié)果返回給 DispatcherServlet。 通過(guò)使用 HandlerAdapter 適配器,DispatcherServlet 不需要關(guān)心具體的 Handler 類型,只需要調(diào)用適配器的方法即可,實(shí)現(xiàn)了對(duì)不同類型 Handler 的統(tǒng)一調(diào)用和處理。
Q5:為什么會(huì)將請(qǐng)求涉及到的攔截器和 Handler 一起封裝?
將請(qǐng)求涉及到的攔截器和Handler一起封裝的目的是為了在請(qǐng)求處理過(guò)程中能夠方便地對(duì)請(qǐng)求進(jìn)行攔截和處理。 攔截器是用于對(duì)請(qǐng)求進(jìn)行預(yù)處理和后處理的組件,可以在請(qǐng)求到達(dá)Controller之前和之后執(zhí)行額外的邏輯。通過(guò)將攔截器和Handler一起封裝,可以實(shí)現(xiàn)以下幾個(gè)方面的功能:
- 統(tǒng)一管理攔截器:將攔截器和Handler一起封裝,可以方便地統(tǒng)一管理攔截器的配置和使用。在配置文件或注解中指定攔截器的順序和范圍,可以靈活地控制攔截器的執(zhí)行順序和作用范圍。
- 方便攔截器的調(diào)用:將攔截器和Handler一起封裝后,可以在請(qǐng)求到達(dá)Controller之前和之后分別調(diào)用攔截器的預(yù)處理和后處理方法。這樣可以方便地對(duì)請(qǐng)求進(jìn)行攔截和處理,執(zhí)行一些通用的操作,如權(quán)限驗(yàn)證、日志記錄等。
- 提高代碼的復(fù)用性:將一些通用的邏輯處理抽離出來(lái)作為攔截器,通過(guò)封裝攔截器和Handler,可以在多個(gè)請(qǐng)求處理過(guò)程中共享這些邏輯,提高代碼的復(fù)用性。例如,多個(gè)請(qǐng)求需要進(jìn)行權(quán)限驗(yàn)證,可以將權(quán)限驗(yàn)證的邏輯抽象為一個(gè)攔截器,在多個(gè)請(qǐng)求處理過(guò)程中共享使用。
- 靈活控制請(qǐng)求處理流程:通過(guò)封裝攔截器和Handler,可以靈活控制請(qǐng)求處理流程。在攔截器的預(yù)處理方法中,可以決定是否繼續(xù)處理請(qǐng)求,或者進(jìn)行一些重定向或錯(cuò)誤處理操作。在攔截器的后處理方法中,可以對(duì)響應(yīng)結(jié)果進(jìn)行進(jìn)一步的處理或修改。
四、設(shè)計(jì)模式
Q1:Spring框架中用到了哪些設(shè)計(jì)模式?
- 工廠模式:通過(guò)beanFactory、ApplicationContext創(chuàng)建bean對(duì)象
- 代理模式:Spring AOP功能的實(shí)現(xiàn)
- 單例模式:Spring的bean默認(rèn)都是單例的
- 適配器模式:Spring MVC用到了適配器模式適配handler、Spring AOP的Advice用到了適配器模式適配成MethodInterceptor接口類型的對(duì)象(全都變成方法攔截器對(duì)象)
- 模板模式:以Template結(jié)尾的對(duì)數(shù)據(jù)庫(kù)操作的類
- 包裝器模式:項(xiàng)目需要連接多個(gè)數(shù)據(jù)庫(kù),不同的客戶每次訪問(wèn)可能會(huì)訪問(wèn)不同的數(shù)據(jù)庫(kù)。包裝器模式可動(dòng)態(tài)的切換不同數(shù)據(jù)源。
五、Spring事務(wù)
Q1:事務(wù)的特性(ACID)了解嗎?
- 原子性:事務(wù)是最小的執(zhí)行單位,不允許分割,事物的原子性確保動(dòng)作要么全部完成,要么不起作用
- 一致性:執(zhí)行事務(wù)前后,數(shù)據(jù)保持一致,例如轉(zhuǎn)賬時(shí),無(wú)論事務(wù)是否成功,轉(zhuǎn)賬人和收款人的總額應(yīng)該是不變的
- 隔離性:并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí),一個(gè)用戶的事務(wù)不被其他事務(wù)所干擾,各并發(fā)事務(wù)之間的數(shù)據(jù)庫(kù)是獨(dú)立的
- 持久性:一個(gè)事務(wù)被提交以后,他對(duì)數(shù)據(jù)庫(kù)的改變是持久的,數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)它有任何影響
只有保證了原子性、隔離性、持久性以后,一致性才能得到保障:AID->C
Q2:什么時(shí)候才用事務(wù)的隔離性?
事務(wù)的隔離性是指多個(gè)事務(wù)并發(fā)執(zhí)行時(shí),各個(gè)事務(wù)之間的數(shù)據(jù)應(yīng)該是相互隔離的,一個(gè)事務(wù)的操作不應(yīng)該對(duì)其他事務(wù)的操作產(chǎn)生影響。在并發(fā)環(huán)境下,如果多個(gè)事務(wù)同時(shí)訪問(wèn)和修改共享數(shù)據(jù),可能會(huì)導(dǎo)致數(shù)據(jù)不一致的問(wèn)題。 舉個(gè)例子來(lái)說(shuō)明事務(wù)的隔離性的使用場(chǎng)景: 假設(shè)有一個(gè)電商平臺(tái),用戶可以在平臺(tái)上購(gòu)買商品,而商品的庫(kù)存是需要管理的。當(dāng)用戶購(gòu)買商品時(shí),會(huì)進(jìn)行庫(kù)存的扣減操作。在這個(gè)場(chǎng)景中,如果不考慮事務(wù)的隔離性,可能會(huì)出現(xiàn)以下問(wèn)題:
非隔離性導(dǎo)致的庫(kù)存錯(cuò)誤:假設(shè)有兩個(gè)用戶 A 和 B 同時(shí)購(gòu)買同一件商品,且?guī)齑嬷挥幸患H绻皇褂檬聞?wù)的隔離性,A 和 B 同時(shí)發(fā)起購(gòu)買請(qǐng)求時(shí),在沒(méi)有互斥控制的情況下,可能會(huì)導(dǎo)致庫(kù)存扣減錯(cuò)誤。例如,A 和 B 同時(shí)讀取庫(kù)存數(shù)量為 1,然后都進(jìn)行庫(kù)存扣減,最終庫(kù)存可能會(huì)變成 -1。 為了解決這個(gè)問(wèn)題,可以使用事務(wù)的隔離性。通過(guò)將庫(kù)存扣減操作放在一個(gè)事務(wù)中,并使用合適的隔離級(jí)別,可以避免并發(fā)訪問(wèn)時(shí)的數(shù)據(jù)不一致問(wèn)題。例如,使用數(shù)據(jù)庫(kù)的隔離級(jí)別 SERIALIZABLE,可以確保同一時(shí)間只有一個(gè)事務(wù)能夠訪問(wèn)和修改庫(kù)存數(shù)據(jù),從而避免庫(kù)存錯(cuò)誤問(wèn)題。
Q3:MySQL怎么保證原子性?
保證原子性,就要在異常發(fā)生的時(shí)候?qū)σ呀?jīng)執(zhí)行的操作進(jìn)行回滾,在MySQL中,恢復(fù)機(jī)制是通過(guò)回滾日志實(shí)現(xiàn)的,所有事務(wù)進(jìn)行的修改都會(huì)先記錄到這個(gè)回滾日志中,然后再執(zhí)行相關(guān)的操作。如果執(zhí)行過(guò)程中出現(xiàn)異常,就利用回滾日志中的信息將數(shù)據(jù)回滾到修改之前的樣子。并且,回滾日志會(huì)先把數(shù)據(jù)持久化到硬盤上,這樣就能保證數(shù)據(jù)庫(kù)在事務(wù)執(zhí)行過(guò)程中宕機(jī),未完成的事務(wù)會(huì)自動(dòng)回滾。當(dāng)再次打開(kāi)數(shù)據(jù)庫(kù)時(shí),數(shù)據(jù)庫(kù)會(huì)通過(guò)回滾日志來(lái)查找未完成的事務(wù),并對(duì)其進(jìn)行回滾操作,以保證數(shù)據(jù)的一致性。
到此這篇關(guān)于Spring question問(wèn)題小結(jié)的文章就介紹到這了,更多相關(guān)Spring question內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Jmeter對(duì)接口測(cè)試入?yún)?shí)現(xiàn)MD5加密
這篇文章主要介紹了Jmeter對(duì)接口測(cè)試入?yún)?shí)現(xiàn)MD5加密,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08基于SpringBoot實(shí)現(xiàn)QQ郵箱驗(yàn)證碼注冊(cè)功能
QQ 郵箱是由騰訊公司推出的一款免費(fèi)郵箱服務(wù),它提供了完整的郵件發(fā)送和接收功能,并且還支持多種郵件格式和附件類型,QQ 郵箱還具有強(qiáng)大的反垃圾郵件功能,可以有效地過(guò)濾垃圾郵件,并保護(hù)用戶隱私和安全,所以本文給大家介紹了基于SpringBoot實(shí)現(xiàn)QQ郵箱驗(yàn)證碼注冊(cè)功能2024-11-11Mybatis-plus實(shí)現(xiàn)主鍵自增和自動(dòng)注入時(shí)間的示例代碼
這篇文章主要介紹了Mybatis-plus實(shí)現(xiàn)主鍵自增和自動(dòng)注入時(shí)間的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07java實(shí)現(xiàn)讀取txt文件中的內(nèi)容
本文通過(guò)一個(gè)具體的例子向大家展示了如何使用java實(shí)現(xiàn)讀取TXT文件里的內(nèi)容的方法以及思路,有需要的小伙伴可以參考下2016-03-03