Spring--AOP自动代理

Spring AOP的自动创建代理可分为三种:

  • BeanNameAutoProxyCreator 根据Bean名称创建代理

  • DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理

  • AnnotationAwareAspectJAutoProxyCreator基于Bean中的AspectJ注解进行自动代理

1、BeanNameAutoProxyCreator 根据Bean名称创建代理

1、新建一个学生接口

public interface StudentDao {
   public void find();
   public void update();
   public void save();
   public void delete();
}

2、实现StudentDao接口


public class StudentDaoImpl implements StudentDao {
    public void find() {
        System.out.println("查询学生");
    }
    public void update() {
        System.out.println("修改学生");
    }
    public void save() {
        System.out.println("保存学生");
    }
    public void delete() {
        System.out.println("删除学生");
    }
}

3、再创建一个类CustomerDao:

public class CustomerDao {
    public void find(){
        System.out.println("查询客户");
    }
    public void save(){
        System.out.println("保存客户");
    }
    public void update(){
        System.out.println("修改客户");
    }
    public void delete(){
        System.out.println("删除客户");
    }
}

4、创建一个前置增强类MybeforeAdvice实现MethodBeforeAdvice接口,重写方法

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

public class MybeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("这是前置增强(通知)");
    }
}

5、再写一个环绕增强类吧。。MyAroundAdvice实现MethodInterceptor

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;


public class MyAroundAdvice implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前增强");
        //执行目标方法
        Object obj = invocation.proceed();
        System.out.println("环绕后增强");
        return obj;
    }
}

6、配置文件

    <!--配置目标类-->
    <bean id="studentDao" class="com.aop.demo5.StudentDaoImpl"/>
    <bean id="customerDao" class="com.aop.demo5.CustomerDao"/>
    <!--配置增强(通知)-->
    <bean id="myBeforeAdvice" class="com.aop.demo5.MybeforeAdvice"/>
    <bean id="myAroundAdvice" class="com.aop.demo5.MyAroundAdvice"/>

    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    	<!--以BeanName创建代理,*Dao就是所有以Dao结尾的BeanId-->
        <property name="beanNames" value="*Dao"/>
        <property name="interceptorNames" value="myBeforeAdvice"/>

    </bean>

7、测试


    @Test
    public void demo1(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext4.xml");

        StudentDao studentDao = (StudentDao) applicationContext.getBean("studentDao");
        CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDao");

        studentDao.save();
        studentDao.find();
        studentDao.delete();
        studentDao.update();
        customerDao.save();
        customerDao.find();
        customerDao.delete();
        customerDao.update();
    }

DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理(根据切面信息)

只需改变配置文件

<!--配置目标类-->
    <bean id="studentDao" class="com.aop.demo5.StudentDaoImpl"/>
    <bean id="customerDao" class="com.aop.demo5.CustomerDao"/>
    <!--配置增强(通知)-->
    <bean id="myBeforeAdvice" class="com.aop.demo5.MybeforeAdvice"/>
    <bean id="myAroundAdvice" class="com.aop.demo5.MyAroundAdvice"/>
        <!--自动代理1:根据Bean名称创建代理-->
    <!--<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*Dao"/>
        <property name="interceptorNames" value="myBeforeAdvice"/>
    </bean>-->
    <!--配置切面,-->
    <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    	<!--指定增强方法需要使用转义字符-->
        <property name="pattern" value="com\.aop\.demo5\.CustomerDao\.save"/>
        <!--选择增强方法-->
        <property name="advice" ref="myAroundAdvice"/>
    </bean>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

AnnotationAwareAspectJAutoProxyCreator基于Bean中的AspectJ注解进行自动代理

需要jar:

  • spring-aop
  • com.springsource.org.aopalliance
  • spring-aspects
  • com.springsource.org.aspectj.wever
    环境准备
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--开启AspectJ的注解开发,自动代理-->
        <aop:aspectj-autoproxy/>
        
</beans>

@AspectJ提供不同的通知类型

  • @Before 前置通知,相当于BeforeAdvice
  • @AfterReturning 后置通知,相当于AfterReturningAdvice
  • @Around 环绕通知,相当于MethodInterceptor
  • @AfterThrowing 异常抛出通知,相当于ThrowAdvice
  • @After最终final通知,不管是否异常,该通知都会执行
  • @DeclareParents 引介通知,相当于IntroductionInterceptor

在通知中通过value属性定义切点
通过execution函数,可以定义切点的方法切入

  • 语法 execution(<访问修饰符>?<返回值类型><方法名>(<参数>)<异常>)
    • 例如匹配所有类public方法 execution(public * * (…))
    • 匹配指定包下所有类方法 execution(* com.dao.*( . . ))不包含子包
    • execution(* com.dao…*(…)) …*表示包、子孙包下所有类
    • 匹配指定类所有方法 execution(* com.service.UserService.*(…))
    • 匹配实现特定接口所有类方法 execution(* com.dao.UserDao+.*(…))
    • 匹配所有save开头的方法 execution(* save*(…))

代码演示
1、创建ProductDao

public class ProductDao {
    public void save(){
        System.out.println("保存商品");
    }
    public void update(){
        System.out.println("修改商品");
    }
    public void delete(){
        System.out.println("删除商品");
    }
    public String findOne(){
        System.out.println("查询一个商品");
        return "hello";
    }
    public void findAll(){
        System.out.println("查询所有商品");
        //int i = 1/0;//发生异常通知
    }
}

2、配置文件配置信息,完成自动代理、目标类、切面的定义

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--开启AspectJ的注解开发,自动代理-->
        <aop:aspectj-autoproxy/>

        <!--目标类-->
        <bean id="productDao" class="com.aspectJ.demo1.ProductDao"/>
        <!--定义切面-->
        <bean class="com.aspectJ.demo1.MyAspectAnno"/>
</beans>

3、创建增强类


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 *切面类
 */
@Aspect
public class MyAspectAnno {
    //@Before前置通知:可以在方法中传入JoinPoint对象,用来获得切点信息
    @Before(value = "execution(* com.aspectJ.demo1.ProductDao.save(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("前置通知"+joinPoint);
    }
    //@AfterReturning后置通知:通过return属性可以定义方法返回值,作为参数
    @AfterReturning(value = "execution(* com.aspectJ.demo1.ProductDao.update(..))",returning = "result")
    public void afterReturning(Object result){
        System.out.println("后置通知"+result);
    }
    //@Around环绕通知: around方法的返回值就是目标代理方法执行返回值
    //参数为ProceedingJoinPoint可以调用拦截目标方法执行
    @Around(value = "execution(* com.aspectJ.demo1.ProductDao.delete(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前通知===");
        Object obj = joinPoint.proceed();//执行目标方法,不调用不执行目标方法(即拦截目标方法)
        System.out.println("环绕后通知===");
        return obj;
    }

    //@AfterThrowing 异常通知:当目标方法发生异常时调用,可输出异常信息
    @AfterThrowing(value = "execution(* com.aspectJ.demo1.ProductDao.findAll(..))",throwing = "e")
    //设置参数,打印异常信息
    public void afterThrowing(Throwable e){
        //目标类发生异常
        System.out.println("异常通知"+e);
    }

    //@After最终通知:无论是否出现异常,最终通知总是会被执行
    @After(value = "execution(* com.aspectJ.demo1.ProductDao.findAll(..))")
    public void after(){
        System.out.println("最终通知");
    }

}

4、测试
jar要求:junit版本要求4以上,并且要与spring-test版本一致

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext5.xml")
public class SpringDemo1 {
    @Resource(name = "productDao")
    private ProductDao productDao;

    @Test
    public void demo1(){
        productDao.update();
        productDao.save();
        productDao.findAll();
        productDao.findOne();
        productDao.delete();
    }
}

总结
在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义

切点方法

private void 无参数方法,方法名为切入点。当通知多个切点时,可以使用 || 进行连接

使用前:

    //@Before前置通知:可以在方法中传入JoinPoint对象,用来获得切点信息
    @Before(value = "execution(* com.aspectJ.demo1.ProductDao.save(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("前置通知"+joinPoint);
    }

使用后。方便对具有相同通知类型进行统一管理

    @Pointcut("execution(* com.aspectJ.demo1.ProductDao.save(..))")
    public void myPointcut1(){}
    
    //@Before前置通知:可以在方法中传入JoinPoint对象,用来获得切点信息
    @Before(value = "myPointcut1()")
    public void before1(JoinPoint joinPoint){
        System.out.println("前置通知"+joinPoint);
    }

使用aspectJ的xml方式开发

1、新建一个接口CustomerDao

public interface CustomerDao {
    public void save();
    public void delete();
    public void findOne();
    public String update();
    public  void findAll();
}

2、新建CustomerDao实现类CustomerDaoImpl


public class CustomerDaoImpl implements CustomerDao {
    public void save() {
        System.out.println("保存客户");
    }

    public void delete() {
        System.out.println("删除客户");
       // int i=1/0;
    }

    public void findOne() {
        System.out.println("查询单个客户");
    }

    public String update() {
        System.out.println("修改用户");
        return "Spring";
    }

    public void findAll() {
        System.out.println("查询所有用户");
    }
}

3、新建增强(通知)类


import org.aspectj.lang.ProceedingJoinPoint;


public class MyAspectXML {

    public void before(){
        System.out.println("前置增强");
    }
    //获取返回值,参数名与XML配置文件returning属性要相同
    public void afterReturning(Object result){
        System.out.println("后置增强"+result);
    }

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前通知===");
        Object obj = joinPoint.proceed();//执行目标方法,不调用不执行目标方法(即拦截目标方法)
        System.out.println("环绕后通知===");
        return obj;
    }

    public void afterThrowing(Throwable e){
        //目标类发生异常
        System.out.println("异常通知"+e.getMessage());
    }
    public void after(){
        System.out.println("最终通知");
    }


}

4、配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
       <!-- 使用XML配置方式完成AOP的开发-->
        <!--配置目标类-->
        <bean id="customerDao" class="com.aspectJ.demo2.CustomerDaoImpl"/>
        <!--配置切面类-->
        <bean id="myAspectXML" class="com.aspectJ.demo2.MyAspectXML"/>

        <!--aop的相关配置-->
        <aop:config>
                <aop:pointcut id="pointcut1" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.save(..))"/>
                <aop:pointcut id="pointcut2" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.update(..))" />
                <aop:pointcut id="pointcut3" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.findOne(..))" />
                <aop:pointcut id="pointcut4" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.delete(..))" />
                <aop:pointcut id="pointcut5" expression="execution(* com.aspectJ.demo2.CustomerDaoImpl.delete(..))" />
                <!--配置切面-->
                <aop:aspect ref="myAspectXML">
                  <!--配置前置通知,method与切面类增强方法名相同-->
                  <aop:before method="before" pointcut-ref="pointcut1"/>
                  <aop:after-returning method="afterReturning" pointcut-ref="pointcut2" returning="result"/>
                  <aop:around method="around" pointcut-ref="pointcut3"/>
                  <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/>
                  <aop:after method="after" pointcut-ref="pointcut5"/>
                </aop:aspect>
        </aop:config>
</beans>

5、测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext6.xml")
public class SpringDemo2 {
    @Resource(name = "customerDao")
    private CustomerDao customerDao;

    @Test
    public void demo1(){
        customerDao.save();
        customerDao.update();
        customerDao.findOne();
        customerDao.delete();
        customerDao.findAll();

    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中,AOP(面向切面编程)是一种强大的技术,可以在不修改原始代码的情况下增强应用程序的功能。它是一种编程范式,可以将横切关注点(例如日志记录、事务管理、安全性等)从应用程序的主逻辑中分离出来。 以下是在Spring Boot中使用AOP的入门步骤: 1. 添加依赖 您需要在pom.xml文件中添加以下依赖项: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 2. 创建一个切面类 您需要创建一个切面类来定义要应用于应用程序的横切关注点。切面类应该使用@Aspect注解进行注释,并且应该包含一个或多个使用@Before、@After、@Around等注解进行注释的通知方法。 例如,以下是一个简单的切面类: ```java @Aspect @Component public class MyAspect { @Before("execution(* com.example.demo.MyService.*(..))") public void beforeAdvice() { System.out.println("Before advice called."); } @After("execution(* com.example.demo.MyService.*(..))") public void afterAdvice() { System.out.println("After advice called."); } } ``` 在上面的示例中,@Before和@After注释用于定义在执行MyService类中的任何方法之前和之后要执行的通知方法。 3. 将切面类注册到Spring Boot应用程序中 您需要将切面类注册到Spring Boot应用程序中,以便它可以在应用程序执行期间应用于横切关注点。您可以使用@Configuration注解或@ComponentScan注解来注册切面类。 例如,以下是使用@Configuration注解注册切面类的示例: ```java @Configuration @EnableAspectJAutoProxy public class AppConfig { @Bean public MyAspect myAspect() { return new MyAspect(); } } ``` 在上面的示例中,@EnableAspectJAutoProxy注解用于启用Spring Boot的自动代理功能,以便Spring Boot可以在运行时创建代理对象。@Bean注解用于将MyAspect类注册为Spring Boot应用程序的bean。 4. 测试切面 一旦您已经注册了切面类并启用了自动代理功能,您可以测试切面是否按预期工作。您可以创建一个MyService类来模拟应用程序的主逻辑,并在其中调用一个或多个方法。 例如,以下是一个简单的MyService类: ```java @Service public class MyService { public void doSomething() { System.out.println("Doing something."); } } ``` 在上面的示例中,MyService类具有一个名为doSomething()的方法,用于模拟应用程序中的主逻辑。 您可以创建一个Spring Boot测试类来测试切面是否按预期工作。例如,以下是一个简单的测试类: ```java @RunWith(SpringRunner.class) @SpringBootTest public class MyServiceTest { @Autowired private MyService myService; @Test public void testDoSomething() { myService.doSomething(); } } ``` 在上面的示例中,@Autowired注解用于将MyService类注入到测试类中。在testDoSomething()方法中,您可以调用MyService类的doSomething()方法,以便切面可以应用于该方法。 如果一切正常,您应该在控制台上看到以下输出: ``` Before advice called. Doing something. After advice called. ``` 这表明切面已成功应用于doSomething()方法,并且切面中定义的通知方法已成功调用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值