Spring学习个人总结(二):aop面向切面编程

aop简介:

利用aop可以将业务的各个部分分离,实现在不改变源方法的情况下对方法进行增强,aop是动态代理的一个应用

动态代理代码案例:

模拟用户登录,源登录方法只有检查用户名和密码的代码,没有实现检测用户类型的代码。而原方法不可以进行直接修改,这是我们就可以用动态代理实现
1.定义一个接口和实现类,内部有登录方法
为了方便,就不进行数据库操作,直接定义一个用户名和密码

//接口
public interface UserDao {
    //用户登录
    boolean login(UserData userData);
}

//实现类
public class UserImply implements UserDao {
    //模拟数据库的查询结果
    private static final String username="admin";
    private static final String password="123";
    /*
    源登陆方法
     */
    @Override
    public boolean login(UserData userData) {
        if(username.equals(userData.getName())&&password.equals(userData.getPassword())){
            System.out.println("登录成功");
            return true;
        }else {
            System.out.println("用户名或密码错误,请重试!");
            return false;
        }
    }
}

现在我们来增强login方法,判断用户类型(管理员或普通用户)
定义一个增强方法所在的类,需要实现InvocationHandler接口

class ProxyCommon implements InvocationHandler{
    private UserDao userDao;
    public ProxyCommon(UserDao userDao){
        this.userDao=userDao;
    }
    /**
     *
     * @param proxy 代理对象(一般用不到)
     * @param method 方法
     * @param args 参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //通过反射调用login方法
        if("login".equals(method.getName())) {
            UserData userData= (UserData) args[0];
            boolean invoke = (boolean) method.invoke(userDao, args);
            if (invoke){//通过login方法判断是否登陆成功
                //登陆成功则判断用户类型
                if("管理员".equals(userData.getType())){
                    System.out.println("您是管理员"+userData.getName()+",将为您跳转到后台管理页面...");
                }else {
                    System.out.println("您好普通用户"+userData.getName());
                }
                return true;
            }else {
                return false;
            }
        }
        return null;
    }
}

测试:

public static void main(String[] args) {
        //首先实例化两个测试用户
        UserData commonUser = new UserData("admin","123","普通用户");
        UserData adminUser = new UserData("admin","123","管理员");
        //实例化需要强化的类,该类的login方法需要强化
        UserDao userImply = new UserImply();
        /*
        使用proxy进行代理,第一个参数是类加载器,第二个参数是类的接口,第三个是代理类
         */
        UserDao newUserImpl = (UserDao) Proxy.newProxyInstance(userImply.getClass().getClassLoader(),
                userImply.getClass().getInterfaces(), new ProxyCommon(userImply));
        //普通用户登录
        newUserImpl.login(commonUser);
        //管理员登陆
        newUserImpl.login(adminUser);
    }

运行结果:
在这里插入图片描述
有以上运行结果:方法login不再只输出登陆成功或者失败,又增加了判断用户类型的功能,可见方法得到了增强;

spring中的动态代理aop

aop中的术语:

  1. 连接点:可以被增强的方法称为连接点
  2. 切入点:实际被增强的方法称为切入点
  3. 通知(增强):实际增强的逻辑部分称为通知,通知分为五种
    1.前置通知
    2.后置通知
    3.异常通知
    4.环绕通知
    5.最终通知
  4. 切面:把通知应用到切入点的过程

切入点表达式
作用:知道哪个类中的那个方法进行增强
语法:
execution(【权限修饰符】【返回值类型】【类的全路径】【方法名】(【参数列表】))

1.建一个需要增强的类

@Component
public class BookImpl{
    public void add() {
        System.out.println("add方法执行...");
    }
}

2.建一个增强类,增强类需要添加@Aspect注解,标注这是个增强类

@Component
@Aspect
public class ProxyBook {
    /*
    编写切入点表达式,将他放在一个空方法上
    后续的通知只需要在注解中标明方法即可将切入点表达式注入
     */
    @Pointcut("execution(* com.zhang.aspectj.BookImpl.*(..))")
    public void pointcut(){}
    //前置通知
    @Before("pointcut()")
    public void before(){
        System.out.println("前置通知执行...");
    }
}

在配置文件中开启注解扫描和aop注解支持

<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.zhang"/>
    <aop:aspectj-autoproxy/>
</beans>

测试:

@Test
    public void aspect(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");
        BookImpl bookImpl = applicationContext.getBean("bookImpl", BookImpl.class);
        bookImpl.add();
    }

在这里插入图片描述
除了@Before前置通知外还有其他的通知:

  1. @After:后置通知,方法执行后
  2. @AfterThrowing:异常通知,方法抛出异常时执行,所以@After和它只能执行一个
  3. @AfterReturning:最终通知,最后一定执行
  4. @Around:环绕通知,用法如下:

@Around

@Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("环绕之前...");
        //执行需要增强的方法
        joinPoint.proceed();
        System.out.println("环绕之后...");
    }

最后将所有通知结合测试:

@Component
@Aspect
public class ProxyBook {
    /*
    编写切入点表达式,将他放在一个空方法上
    后续的通知只需要在注解中标明方法即可将切入点表达式注入
     */
    @Pointcut("execution(* com.zhang.aspectj.BookImpl.*(..))")
    public void pointcut(){}
    //前置通知
    @Before("pointcut()")
    public void before(){
        System.out.println("前置通知执行...");
    }
    //后置通知
    @After("pointcut()")
    public void after(){
        System.out.println("后置通知执行...");
    }
    //最终通知
    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("最终通知执行...");
    }
    //异常通知
    @AfterThrowing("pointcut()")
    public void afterThrowing(){
        System.out.println("异常通知执行...");
    }
    //环绕通知
    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("环绕之前...");
        //执行需要增强的方法
        joinPoint.proceed();
        System.out.println("环绕之后...");
    }
}

运行结果:
在这里插入图片描述
如果增强类有多个,并且都增强了同一个方法,这是可以在每个增强类上增加@Order注解,里面指定一个数值,数值越小,优先级越高

总结:

使用aop总共有这几步:

  1. 导入spring-aop,spring-aspects,aspectjwear坐标
  2. 编写增强类
  3. 在类上添加@Aspect注解
  4. 编写切入点表达式
  5. 编写增强方法,使用注解指明通知类型
  6. 在xml中开启aop注解支持
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值