• 阶段七-Day02-Spring02


    一、Spring的注解支持

    1. 为什么使用Spring注解

    在昨天的练习中有这样的一段代码,为了给UserServiceImpl注入UserMapper对象。

    2. Spring支持的注解(IoC/DI相关)

    下面@Repository、@Service、@Controller、@Configuration都是@Component注解的子注解,作用相同。

    主要的区别是语义上的区别。当看到不同的注解放在不同层的类中。但是不按照语义去做,非把@Service用在持久层,也是有效果的。但是这样却是不规范的。

    注解名称解释
    @Component实例化Bean,默认名称为类名首字母变小写(类名不要出现类似AServiceImpl)。支持自定义名称
    @Repository@Component子标签,作用和@Component一样,用在持久层。
    @Service@Component子标签,作用和@Component一样,用在业务层。
    @Controller@Component子标签,作用和@Component一样,用在控制器层。
    @Configuration@Component子标签,作用和@Component一样,用在配置类。
    @Autowired自动注入。默认byType,如果多个同类型bean,使用byName(默认通过属性名查找是否有同名的bean,也可以通过@Qualifier("bean名称"),执行需要注入的Bean名称)
    @Resource非Spring注解。默认byName,如果没找到,使用byType。 javax.annotation
    javax.annotation-api1.3.2

    二、Spring Test 模块

    1. 介绍

    Spring Test 模块整合了一些常见的单元测试工具,例如Junit。

    整合后可以在测试类中直接使用Spring容器中的内容,把测试类也放入到Spring容器中,测试类里面可以直接使用注解注入容器中的bean对象。

    同时也可以通过@ContextConfigration注解指定配置文件路径,让测试方法在启动的时候直接加载配置文件。

    2. 添加依赖

    1. spring-context核心依赖无论使用哪个模块都必须导入的。

    2. junit依赖需要导入的,现在使用的就是Spring Test模块整合的junit功能。

    3. spring-test 表示spring test模块的依赖,导入后才有@ContextConfiguration这些注解。

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframeworkgroupId>
    4. <artifactId>spring-contextartifactId>
    5. <version>5.3.16version>
    6. dependency>
    7. <dependency>
    8. <groupId>junitgroupId>
    9. <artifactId>junitartifactId>
    10. <version>4.13.2version>
    11. <scope>testscope>
    12. dependency>
    13. <dependency>
    14. <groupId>org.springframeworkgroupId>
    15. <artifactId>spring-testartifactId>
    16. <version>5.3.16version>
    17. <scope>testscope>
    18. dependency>
    19. dependencies>

    3. 编写测试

    该注解会自动创建spring容器,管理MyTest类

    1. @RunWith(SpringJunit4ClassRunner.class)
    2. @ContextConfiguration(locations = "classpath:spring.xml")
    3. public class MyTest {
    4. @Autowired
    5. PeopleService peopleService;
    6. @Test
    7. public void test2(){
    8. peopleService.test();
    9. }
    10. }

    三、代理设计模式

    1.引入

    但是在实际生产环境中,我们发现随着公司业务的增长,我们会对现有的功能方法进行功能升级,就是保留原有功能的基础上增加新的逻辑,那么大家最容易想到的方案就是找到要升级的功能方法直接修改,但是如果我们调用的是第三方的功能代码,就没有源码,那么没有办法直接修改了,怎么办?而且就算我们有源码,源码也可能是其他人写的,这时候去修改源码,就需要花费大量的时间去阅读源码的逻辑,非常麻烦,效率又低,怎么办?

    我们可以在A和B插入一个对象C,让A不再直接调用B,而是调用对象C,在C中调用对象B的testB方法,在C对象中调用对象B的方法之前和之后可以添加我们的扩展代码即可!

    2. 介绍

    代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。为什么要采用这种间接的形式来调用对象呢?一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。

    代理设计模式包括:静态代理和动态代理。

    静态代理:代理对象由程序员自己编写!

    动态代理: 代理对象是动态产生的,动态代理又分为JDK动态代理和Cglib动态代理两种实现方式。其中JDK动态代理是基于接口实现,也就是代理对象和真实对象需要实现相同的接口,JDK动态代理是Java官方提供的技术。Cglib动态代理是基于继承实现的,也就是代理对象需要继承真实对象,Cglib动态代理是第三方的技术,使用的时候需要导入jar包。

     静态代理示例

    创建接口MyInterface

    ​ public interface MyInterface {    void testMethod(); }

    创建真实对象类

    //真实对象 public class MyTrueClass implements MyInterface{  

     @Override    

    public void testMethod() {        

    System.out.println("我是方法.....");

      }

    }

    创建代理对象类

    //代理对象 public class MyStaticProxyClass implements MyInterface {    

    //代理方法    

    @Override    

    public void testMethod() {        

    //其他代码        

    System.out.println("扩展代码上...");      

     //调用真实对象的真实方法        

    MyTrueClass myTrueClass=new MyTrueClass();        

    myTrueClass.testMethod();      

     //其他代码        

    System.out.println("扩展代码下....");  

        }

    }

    3. 动态代理示例

    3.1 JDK动态代理

    JDK动态代理是基于接口来实现的,被代理的类必须实现了接口。首先我们需要创建接口。

    创建接口

    1. public interface MyInterfaceJdk {
    2. void testJdkMethod();
    3. }

    创建实现类

    1. public class MyInterfaceJdkImpl implements MyInterfaceJdk {
    2. @Override
    3. public void testJdkMethod() {
    4. System.out.println("我是真实的方法...");
    5. }
    6. }
    测试代码
    1. public class TestProxy {
    2. @Test
    3. public void testJdkProxy(){
    4. //创建被代理对象
    5. MyInterfaceJdkImpl myInterfaceJdk = new MyInterfaceJdkImpl();
    6. //创建代理对象
    7. /*
    8. * 参数1: 类加载器。使用指定的类加载器加载动态创建的代理对象。
    9. * 参数2: 被代理类实现的接口。
    10. * 参数3: 动态代理对象会自动调用InvocationHandler中的invoke()方法,
    11. * 需要在invoke方法中编写对应逻辑代码及执行目标对象中的目标方法。
    12. * */
    13. MyInterfaceJdk o = (MyInterfaceJdk) Proxy.newProxyInstance(
    14. TestProxy.class.getClassLoader(),
    15. new Class[]{MyInterfaceJdk.class},
    16. new InvocationHandler() {
    17. @Override
    18. public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
    19. System.out.println("目标方法之前执行");
    20. method.invoke(myInterfaceJdk, objects);
    21. System.out.println("目标方法之后执行");
    22. return null;
    23. }
    24. }
    25. );
    26. o.testJdkMethod();
    27. }
    28. }
    3.2 Cglib动态代理

    Cglig动态是第三方提供的技术,需要导入jar包,并且是基于类的继承。

    1. <dependency>
    2. <groupId>cglibgroupId>
    3. <artifactId>cglibartifactId>
    4. <version>3.3.0version>
    5. dependency>

    测试代码

    1. package com.sh;
    2. import com.sh.service.UserService;
    3. import com.sh.service.UserServiceImp;
    4. import net.sf.cglib.proxy.Enhancer;
    5. import net.sf.cglib.proxy.MethodInterceptor;
    6. import net.sf.cglib.proxy.MethodProxy;
    7. import org.junit.Test;
    8. import java.lang.reflect.InvocationHandler;
    9. import java.lang.reflect.Method;
    10. import java.lang.reflect.Proxy;
    11. public class UserTest {
    12. /*
    13. * 动态代理: 程序运行时动态创建代理对象
    14. *
    15. * 1.JDK动态代理: 基于接口实现: 底层使用反射来创建代理对象
    16. * 1.为接口创建动态代理对象(实现接口中的方法,相当于实现类的功能)
    17. * 2.为接口的实现类创建动态的代理对象 (中间调用目标方法,实现功能的增强)
    18. *
    19. *
    20. * */
    21. //jdk 只能基于接口通过反射来创建代理对象
    22. @Test
    23. public void test(){
    24. //JDK动态代理实现类
    25. UserService userService = (UserService) Proxy.newProxyInstance(
    26. //参数一:类加载器
    27. UserTest.class.getClassLoader(),
    28. //参数二:被代理类实现的接口数组
    29. new Class[]{UserService.class},
    30. //参数三: 运行时底层重写的方法内容
    31. new InvocationHandler() {
    32. @Override
    33. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    34. //还要完成之前实现类的方法
    35. System.out.println("自动实现的方法");
    36. return null;
    37. }
    38. }
    39. );
    40. userService.select();
    41. }
    42. @Test
    43. public void test2(){
    44. UserServiceImp usi = new UserServiceImp();
    45. //JDK动态代理类,扩展方法
    46. UserService userService = (UserService) Proxy.newProxyInstance(
    47. //参数一:类加载器
    48. UserTest.class.getClassLoader(),
    49. //参数二:被代理类实现的接口数组
    50. //通过扩展功能的实现类获取实现的接口
    51. UserServiceImp.class.getInterfaces(),
    52. new InvocationHandler() {
    53. @Override
    54. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    55. System.out.println("前置扩展");
    56. //执行想要扩展功能的实现类原本的方法
    57. //需要先创建该对象
    58. method.invoke(usi);
    59. System.out.println("后置扩展");
    60. return null;
    61. }
    62. }
    63. );
    64. userService.select();
    65. }
    66. @Test
    67. public void test3(){
    68. UserServiceImp usi = new UserServiceImp();
    69. //JDK动态代理类,扩展方法
    70. UserService userService = (UserService) Proxy.newProxyInstance(
    71. //参数一:类加载器
    72. UserTest.class.getClassLoader(),
    73. //参数二:被代理类实现的接口数组
    74. //通过扩展功能的实现类获取实现的接口
    75. UserServiceImp.class.getInterfaces(),
    76. new InvocationHandler() {
    77. @Override
    78. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    79. System.out.println("前置扩展");
    80. //执行想要扩展功能的实现类原本的方法
    81. //需要先创建该对象
    82. //args传递参数把usi的参数传递给代理对象
    83. Object invoke = method.invoke(usi, args);
    84. System.out.println("后置扩展");
    85. return invoke;
    86. }
    87. }
    88. );
    89. String te = userService.te(2);
    90. System.out.println(te);
    91. }
    92. /*
    93. * 2. cglib动态代理: 基于接口或者继承. 底层通过修改字节码文件来创建代理对象
    94. * 1.为接口创建代理对象,相当于实现类
    95. * 2.为接口的实现类创建代理对象,实现功能的增强
    96. * 3.通过继承给实现类创建代理对象,实现功能的增强
    97. * */
    98. //cglib创建
    99. //通过接口实现
    100. //1.为接口创建代理对象
    101. @Test
    102. public void test4(){
    103. //1.创建增强器
    104. Enhancer enhancer = new Enhancer();
    105. //2.设置需要创建动态代理对象的接口
    106. //参数: 接口类对象的数组
    107. enhancer.setInterfaces(new Class[]{UserService.class});
    108. //3.设置回调的方法
    109. /*
    110. * 程序运行过程中创建接口的代理对象,实现接口中的方法,该过程不可见
    111. * 在实现的方法中调用指定的方法
    112. * */
    113. //参数:Callback是接口,使用匿名内部类来创建
    114. //MethodInterceptor是Callback的实现类
    115. enhancer.setCallback(new MethodInterceptor() {
    116. @Override
    117. /*
    118. * Object o:目标对象
    119. * Method method:目标方法对象
    120. * Object[] objects:目标方法的参数
    121. * MethodProxy methodProxy:代理方法对象
    122. * */
    123. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    124. System.out.println("执行了");
    125. return null;
    126. }
    127. });
    128. //4.获取代理对象
    129. UserService o = (UserService) enhancer.create();
    130. o.select();
    131. }
    132. //2.为接口的实现类创建代理对象
    133. @Test
    134. public void test5(){
    135. UserServiceImp userServiceImp = new UserServiceImp();
    136. //1.创建增强器
    137. Enhancer enhancer = new Enhancer();
    138. //2.设置接口
    139. enhancer.setInterfaces(new Class[]{UserService.class});
    140. //3.设置回调方法
    141. enhancer.setCallback(new MethodInterceptor() {
    142. @Override
    143. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    144. System.out.println("前置增强");
    145. //通过目标方法对象来调用目标方法
    146. //参数: Object 目标对象 Object[] 目标对象方法参数
    147. method.invoke(userServiceImp, objects);
    148. System.out.println("后置增强");
    149. return null;
    150. }
    151. });
    152. //4.获取代理对象
    153. //用接口来接收
    154. UserService o = (UserService) enhancer.create();
    155. o.select();
    156. }
    157. //3.通过继承实现类创建代理对象
    158. @Test
    159. public void test6(){
    160. //1.创建增强器
    161. Enhancer enhancer = new Enhancer();
    162. //2.设置父类
    163. enhancer.setSuperclass(UserServiceImp.class);
    164. //3.设置回调方法
    165. enhancer.setCallback(new MethodInterceptor() {
    166. @Override
    167. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    168. System.out.println("执行了");
    169. //调用实现类的方法
    170. //通过代理的方法对象调用父类的方法
    171. //类似super
    172. methodProxy.invokeSuper(o,objects);
    173. return null;
    174. }
    175. });
    176. //4.
    177. UserServiceImp o = (UserServiceImp) enhancer.create();
    178. o.select();
    179. }
    180. }
    3.3 动态代理总结
    1. JDK动态代理机制是委托机制,只能对实现了接口的类生成代理,底层通过反射机制实现。

    2. CGLIB动态代理机制是继承机制,针对类生成代理,被代理类和代理类是继承关系,底层通过字节码处理框架asm,修改字节码生成子类。

    四、SpringAOP介绍

    1. SpringAOP介绍(常见面试题)

    面向切面编程 (AOP) 通过提供另一种思考程序结构的方式来补充面向对象编程 (OOP)。OOP 中模块化的关键单位是类,而 AOP 中模块化的单位是切面。切面能够实现跨越多种类型和对象的关注点(例如事务管理)的模块化。(这种关注点在 AOP 文献中通常被称为“横切”关注点。)

    Spring 的关键组件之一是 AOP 框架。虽然 Spring IoC 容器不依赖于 AOP(意味着如果您不想使用 AOP,则不需要使用 AOP),但 AOP 补充了 Spring IoC 以提供一个非常强大的中间件解决方案。

    官方在强调AOP时强调了下面几点:

    1. AOP 叫做面向切面编程。

    2. AOP 是对OOP的补充。

    3. AOP的核心是切面。

    4. AOP是对IoC的补充。

    AOP中的专业术语:

    Aspect:切面。为方法添加增强功能的过程

    join point:切入点。就是我们平时说的目标方法,或说对哪个方法做扩展,做增强。

    Advice:通知。就是增强内容

    Pointcut:切点表达式(通过切点表达式可以找到需要增强功能的方法),通过表达式说明哪些方法是join point。

    AOP Proxy:代理。Spring支持JDK动态代理和cglib动态代理两种方式,可以通过proxy-target-class=true把默认的JDK动态代理修改为Cglib动态代理

    Weaving:织入。织入就是把Advice添加到join point的过程。

    如果面试官问:什么是AOP?

    AOP叫做面向切面编程,属于对OOP的扩展。其实现是基于动态代理设计模式,在IoC基础上实现的。

    AOP就是对某个切入点做了通知进行增强扩展,形成横切面。可以实现在不修改原有代码的情况下,做额外扩展。

    2. 实现AOP的两种方式

    在Spring中提供了两种方式实现AOP:

    • Schema-based:所有的通知都需要实现特定类型的接口实现通知。

    • AspectJ:可以使用普通Java类结合特定的配置实现通知。

    3. AOP底层代理模式

    SpringAOP底层默认使用的JDK动态代理,但是同时也支持cglib动态代理。

    需要配置Cglib的依赖以及在Spring的配置文件中开启Cglib动态代理。

    五、Schema-based方式实现AOP

    1. 在Schema-based方式中通知的分类(面试题)

    • 前置通知:在切入点之前执行的增强功能。通知需要实现MethodBeforeAdvice接口。

    • 后置通知:在切入点之后执行的增强功能。通知需要实现AfterReturningAdvice接口。

    • 环绕通知:一个方法包含了前置通知和后置通知的功能。通知需要实现MethodInterceptor接口。

    • 异常通知:如果切入点中出现了异常(绝对不能try...catch解决了异常)就会触发异常通知。通知需要实现ThrowsAdvice接口。

    注意:添加新的依赖

    1. <dependency>
    2. <groupId>org.aspectjgroupId>
    3. <artifactId>aspectjweaverartifactId>
    4. <version>1.9.9.1version>
    5. dependency>
    6. <dependency>
    7. <groupId>aopalliancegroupId>
    8. <artifactId>aopallianceartifactId>
    9. <version>1.0version>
    10. dependency>

    每种通知都需要设置一个对应的通知类

    2. 前置通知

    前置通知实在切入点之前执行的增强

    2.1 新建通知类

    重写before方法

    1. public class MyBefore implements MethodBeforeAdvice {
    2. @Override
    3. public void before(Method method, Object[] args, Object target) throws Throwable {
    4. System.out.println("前置通知");
    5. }
    6. }

    3. 后置通知

    后置通知是在切入点之后执行的增强。

    3.1 新建通知类

    重写after方法

    1. public class MyAfter implements AfterReturningAdvice {
    2. @Override
    3. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    4. System.out.println("后置通知");
    5. }
    6. }

    4. 环绕通知

    环绕通知可以实现前置通知和后置通知两种功能。

    4.1 新建通知类

    重写invoke方法

    • invocation:方法调用器。通过invocation可以proceed()方法调用执行点。

    1. public class MyAround implements MethodInterceptor {
    2. @Override
    3. public Object invoke(MethodInvocation invocation) throws Throwable {
    4. System.out.println("环绕通知-前置增强");
    5. Object result = invocation.proceed();// 执行切入点
    6. System.out.println("环绕通知-后置增强");
    7. return result;
    8. }
    9. }

    5.异常通知

    异常通知只有在切入点出现异常时才会被触发。如果方法没有异常,异常通知是不会执行的。

    5.1 新建通知类

    MethodInterceptor接口没有方法,但是我们必须严格提供一个下面的方法:

    • public void afterThrowing:必须相同

    • 必须有Exception参数

    要自己写一个下面的方法

    1. public class MyThrow implements ThrowsAdvice {
    2. public void afterThrowing(Exception e){
    3. System.out.println("异常通知:"+e.getMessage());
    4. }
    5. }
    配置切面
    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xmlns:aop="http://www.springframework.org/schema/aop"
    6. xsi:schemaLocation="http://www.springframework.org/schema/beans
    7. https://www.springframework.org/schema/beans/spring-beans.xsd
    8. http://www.springframework.org/schema/context
    9. https://www.springframework.org/schema/context/spring-context.xsd
    10. http://www.springframework.org/schema/aop
    11. https://www.springframework.org/schema/aop/spring-aop.xsd">
    12. <context:component-scan base-package="com.sh"/>
    13. <bean id="mybefore" class="com.sh.advice.MyBefore"/>
    14. <bean id="myafter" class="com.sh.advice.MyAfter"/>
    15. <bean id="myaround" class="com.sh.advice.MyAround"/>
    16. <bean id="mythrow" class="com.sh.advice.MyThrow"/>
    17. <aop:config>
    18. <aop:pointcut id="mypoint" expression="execution(* com.sh.service.impl.PeopleServiceImpl.test())"/>
    19. <aop:advisor advice-ref="mythrow" pointcut-ref="mypoint"/>
    20. aop:config>
    21. beans>

    6. Schema-based方式包含多个通知的执行顺序

    如果切面中包含多个通知,执行顺序是按照配置顺序执行。

    • 前置通知:先配置的先执行。

    • 后置通知:先配置的后执行。

    六、AspectJ实现AOP

    1. 引入介绍

    问题:

    目前我们已经能够使用Schema-based方式来实现AOP。在使用Schema-based方式实现功能扩展时,发现一个扩展代码就需要声明对应的实现了指定的接口的类,这样造成代码的结构体系过于繁杂。一个通知一个类。

    解决:

    在一个类中声明所有的通知方法。但是这样又会造成Spring容器无法区分该类中的方法那些是前置,那些是后置,那些是环绕,那些是异常了,怎么办?在配置文件中的切面配置中,指明哪些方法是前置,哪些是后置即可。

    实现:

    Aspectj方式。

    2. AspectJ方式通知类型(面试题)

    • 前置通知:before。

    • 后置通知:after。

      • after是否出现异常都执行的后置通知。

      • after-returning切入点不出现异常时才执行的后置通知。

    • 环绕通知:around。

    • 异常通知:after-throwing。

    3. 代码实现

    3.1 创建通知类

    Aspectj方式实现AOP的通知类不需要实现任何的接口,直接声明一个普通java类即可,然后在类中直接定义通知方法即可,方法名随意,但是建议方法名见名知意。

    1. public class MyAdvice {
    2. //前置通知方法
    3. public void before(){
    4. System.out.println("我是前置通知方法...");
    5. }
    6. //后置通知方法
    7. public void after(){
    8. System.out.println("我是后置通知方法...");
    9. }
    10. //环绕通知方法
    11. public Object round(ProceedingJoinPoint pp) throws Throwable {
    12. System.out.println("环绕---前");
    13. //放行
    14. Object proceed = pp.proceed();
    15. System.out.println("环绕---后");
    16. return proceed;
    17. }
    18. //异常通知方法
    19. public void myThrow(Exception ex){
    20. System.out.println("我是异常通知......"+ex.getMessage());
    21. }
    22. }
    3.2 配置AOP
    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:aop="http://www.springframework.org/schema/aop"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans
    6. http://www.springframework.org/schema/beans/spring-beans.xsd
    7. http://www.springframework.org/schema/aop
    8. http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
    9. <context:component-scan base-package="com.sh"/>
    10. <bean id="myadvice" class="com.sh.advice.MyAdvice"/>
    11. <aop:config>
    12. <aop:aspect ref="myadvice">
    13. <aop:pointcut id="mp" expression="execution(* com.sh.service.*.*(..))"/>
    14. <aop:before method="before" pointcut-ref="mp"/>
    15. <aop:after-returning method="after" pointcut-ref="mp"/>
    16. <aop:after method="after" pointcut-ref="mp"/>
    17. <aop:after-throwing method="myThrow" pointcut-ref="mp" throwing="ex"/>
    18. aop:aspect>
    19. aop:config>
    20. beans>

    注意:

    after和after-returning,after无论切点是否出现异常都执行的后置通知,after-returning只有在切点正常执行完成,才会触发的通知。

    3. 在通知中获取目标方法的参数

    1. /*
    2. 如果希望获取切入点方法的参数,推荐把通知的参数和切入点的参数写成一致。
    3. */
    4. public class MyAdvice2 {
    5. public void mybefore(int id1, boolean bool1){
    6. System.out.println("前置通知"+id1+","+bool1);
    7. }
    8. public void myafter(int id1, boolean bool1){
    9. System.out.println("后置:"+id1+","+bool1);
    10. }
    11. public void myafter2(int id1, boolean bool1){
    12. System.out.println("后置2:"+id1+","+bool1);
    13. }
    14. public Object myaround(ProceedingJoinPoint pjp,int id1, boolean bool1) throws Throwable {
    15. System.out.println("环绕前置");
    16. Object result = pjp.proceed();
    17. System.out.println("环绕后置");
    18. return result;
    19. }
    20. public void mythrow(Exception ex,int id1, boolean bool1){
    21. System.out.println("异常通知:"+ex.getMessage()+",id:"+id1+",bool1:"+bool1);
    22. }
    23. }

    在配置文件中配置

    arg-names

    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xmlns:aop="http://www.springframework.org/schema/aop"
    6. xsi:schemaLocation="http://www.springframework.org/schema/beans
    7. https://www.springframework.org/schema/beans/spring-beans.xsd
    8. http://www.springframework.org/schema/context
    9. https://www.springframework.org/schema/context/spring-context.xsd
    10. http://www.springframework.org/schema/aop
    11. https://www.springframework.org/schema/aop/spring-aop.xsd">
    12. <context:component-scan base-package="com.sh"/>
    13. <bean id="myadvice2" class="com.sh.advice.MyAdvice2">bean>
    14. <aop:config>
    15. <aop:aspect ref="myadvice2">
    16. <aop:pointcut id="mypoint" expression="execution(* com.bjsxt.service.impl.PeopleServiceImpl.test(int,boolean)) and args(id1,bool1)"/>
    17. <aop:before method="mybefore" pointcut-ref="mypoint" arg-names="id1,bool1"/>
    18. <aop:after-returning method="myafter" pointcut-ref="mypoint" arg-names="id1,bool1"/>
    19. <aop:after method="myafter2" pointcut-ref="mypoint" arg-names="id1,bool1"/>
    20. <aop:around method="myaround" pointcut-ref="mypoint" arg-names="pjp,id1,bool1"/>
    21. <aop:after-throwing method="mythrow" pointcut-ref="mypoint" arg-names="ex,id1,bool1" throwing="ex"/>
    22. aop:aspect>
    23. aop:config>
    24. beans>

    4. Schema-based和Aspectj的区别

    Schema-based:基于接口实现的。每个通知都需要实现特定的接口类型,才能确定通知的类型。由于类已经实现了接口,所以配置起来相对比较简单。尤其是不需要在配置中指定参数和返回值类型。

    AspectJ方式:是基于配置实现的。通过不同的配置标签告诉Spring通知的类型。AspectJ方式对于通知类写起来比较简单。但是在配置文件中参数和返回值需要特殊进行配置。

    因为Schame-based是运行时增强,AspectJ是编译时增强。所以当切面比较少时,性能没有太多区别。但是当切面比较多时,最好选择AspectJ方式,因为AspectJ方式要快很多。

    七、注解方式实现AOP

    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:aop="http://www.springframework.org/schema/aop"
    5. xmlns:context="http://www.springframework.org/schema/context"
    6. xsi:schemaLocation="http://www.springframework.org/schema/beans
    7. http://www.springframework.org/schema/beans/spring-beans.xsd
    8. http://www.springframework.org/schema/aop
    9. http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    10. http://www.springframework.org/schema/context
    11. http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    12. <context:component-scan base-package="com.bjsxt"/>
    13. <aop:aspectj-autoproxy expose-proxy="true"/>
    14. beans>

    @Component

    作用:

    相当于配置文件的bean标签,将某个类的对象扫描到Spring容器中。此注解一般在普通Java类上用。

    @Aspect

    作用:声明该类为通知类。

    使用:结合@Component在通知类上使用,Spring扫描到容器中并作为通知类。

    @Service

    作用:

    相当于配置文件的bean标签,将某个类的对象扫描到Spring容器中。此注解专门在业务层实体类上使用来表明该类为业务类。

    @pointcut

    作用:声明切点。

    使用:方法上使用。

    @Before

    作用:声明方法为前置通知方法。

    使用:在前置通知方法上声明。

    注意:需要在其中声明对应的切点的路径,非同包中需要指定全限定路径。

    @After

    作用:声明方法为后置通知方法。

    使用:在后置通知方法上声明。

    @Around

    作用:声明方法为环绕通知方法。

    使用:在环绕通知方法上声明。

    @AfterThrowing

    作用:声明方法为异常通知方法。

    使用:在异常通知方法上声明。

  • 相关阅读:
    阿里巴巴店铺的所有商品API接口(item_search_shop-获得店铺的所有商品接口),阿里巴巴API接口
    自己动手从零写桌面操作系统GrapeOS系列教程——19.硬盘读写理论知识
    【Jenkins】data stream error|Error cloning remote repo ‘origin‘ 错误解决【亲测有效】
    hexo 使用hexo g -d报错
    算法工程师 | 如何快速 了解,掌握一个算法!脚踏实地,迎着星辰,向前出发 ~
    Vue 前置 后置 路由守卫 独享 路由权限控制 自定义属性
    FITC-PEG-FA,Folic acid-PEG-Fluorescein,叶酸PEG荧光素
    ubuntu 23 使用教程
    关于#debian#的问题:想请问这种情况怎么解决 deb 打包里增加删除之前安装包脚本
    js第一章
  • 原文地址:https://blog.csdn.net/m0_59163770/article/details/133914329