Spring面向切面编程(AOP,Aspect Oriented Programming)

  AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。使用JDK的动态代理可以实现AOP.

  AOP通过代理的方式都程序动态统一添加功能

现在要给功能4增加一些额外的行为,如处理日志,处理权限等,可以使用代理实现。我们在功能4外面包装一个对象,假设叫A,

model原来是直接调用功能4,现在model调用包装对象A,A再调用功能4,A在调用功能4之前和之后可以增加一些功能。具体参看代理模式。

模拟一下

/**
 * 代理模式中的抽象角色
 */
public interface UserService {
    void queryUsers();
    void saveUser();
    void deleteUser();
}
/**
 * 代理模式中的真实角色
 */
public class UserServiceImpl implements UserService {
    @Override
    public void queryUsers() {
        System.out.println("query users..");
    }

    @Override
    public void saveUser() {
        System.out.println("save a user...");
    }

    @Override
    public void deleteUser() {
        System.out.println("delete a user...");
    }
}
/**
 * 代理角色,需要实现同样的接口
 * 这里我们给程序统一的添加了功能
 */
public class UserServiceImplProxy implements UserService {

    private UserService userService;

    //传入的是真实角色
    public UserServiceImplProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void queryUsers() {
        this.pre();
        userService.queryUsers();
        this.post();
    }

    @Override
    public void saveUser() {
        this.pre();
        userService.saveUser();
        this.post();
    }

    @Override
    public void deleteUser() {
        this.pre();
        userService.deleteUser();
        this.post();
    }

    private void pre(){
        System.out.println("operations pre...");
    }

    private void post(){
        System.out.println("operations post...");
    }
}
public class MainTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        // 使用包装之的后的代理对象
        userService = new UserServiceImplProxy(userService);
        userService.saveUser();
        userService.deleteUser();
        userService.queryUsers();

    }
}

这样做有一个不好的地方,

首先,三个方法添加的功能如果是一样的,每个方法里写一遍,麻烦!

其次,接口中如果增加了方法,真实对象和代理对象都要改,新的方法添加同样的功能,再写一遍!!

改进:JDK动态代理,可以给接口中的方法统一添加功能能,而不是在每个方法中都写一遍。

public class LogJDKProxy implements InvocationHandler {

    //要代理的对象
    private Object target;

    public LogJDKProxy(Object target) {
        this.target = target;
    }

    //创建代理对象
    public Object createProxyObject(){
        return Proxy.newProxyInstance(//
                target.getClass().getClassLoader(), //
                target.getClass().getInterfaces(), //
                this);
    }

    /**
     * @param proxy 代理对象
     * @param method 当前要执行的方法,调用谁就是谁
     * @param args  方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.before();
        // 执行原方法
        Object result = method.invoke(target, args);
        this.after();
        return result;
    }

    private void pre(){
        System.out.println("operations pre...");
    }

    private void post(){
        System.out.println("operations post...");
    }
}
public class MainTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        // 使用包装之的后的代理对象
        userService = (UserService) new LogJDKProxy(userService).createProxyObject();
        // 使用包装之的后的代理对象
        userService.saveUser();
        userService.queryUsers();
        userService.deleteUser();
    }
}

执行结果

operations pre...
save a user...
operations post...
operations pre...
query users..
operations post...
operations pre...
delete a user...
operations post...

这就叫动态统一的添加功能

如果接口中增加了方法,代理对象不用变

尽管如此,动态代理还是有一个问题,就是要求被代理的对象要实现接口,因为有这么一句

target.getClass().getInterfaces(),
对于没有实现接口的类也想实现动态代理的效果要怎么办呢?

使用cglib,通过子类的方式生成代理,因为子类可以重写父类的方法,以及调用父类的方法。如

public class UserService{
    public void deleteUser(){
        //......
    }  
}

public class SubUserService extends UserService{
    public void deleteUser(){
        this.pre();
        super.deleteUser()
        this.post();
    }      
}        

代理类

public class LogCglibProxy implements MethodInterceptor {
    //要代理的对象
    private Object target;

    public LogCglibProxy(Object target) {
        this.target = target;
    }

    //创建代理对象
    public Object createProxyObject() {
        Enhancer enhancer = new Enhancer();
        // 设置父类类型
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
            throws Throwable {
        this.pre();
        Object result = method.invoke(target, args);
        this.post();
        return result;
    }

    private void pre() {
        System.out.println("operations pre...");
    }

    private void post() {
        System.out.println("operations post...");
    }
}

这种方式有什么限制呢,jdk动态动态只会给public的方法添加功能。那么CGLIB方式呢?

public class UserService {
    public void foo1() {
        System.out.println(">> public的方法 <<");
    }

    protected void foo2() {
        System.out.println(">> protected的方法 <<");
    }
    //子类不能访问,所以不可以使用cglib
    void foo3() {
        foo3();
        System.out.println(">> default的方法 <<");
    }
  //子类不能访问,所以不可以使用cglib
    private void foo4() {
        System.out.println(">> private的方法 <<");
    }
  //不可重写方法
    public static void foo5() {
        System.out.println(">> static的方法 <<");
    }
  //不可重写方法
    public final void foo6() {
        System.out.println(">> final的方法 <<");
    }
}

cglib的原理是子类重写了父类的方法然后添加功能,所以类不能是final的

AOP是使用代理的方式实现的,它优先使用jdk动态代理,如果没有实现接口,就使用cglib采用子类的方式。

三个重要概念:切入点,切面,通知

切入点:要拦截那些方法

通知:对于拦截的方法他要做什么事

切面 = 切入点 + 通知(对拦截的方法做什么事)

使用编程的方式实现AOP(使用接口)

public interface UserService {

    void queryUsers();

    void saveUser();

    void deleteUser();
}
public class UserServiceImpl implements UserService {

    @Override
    public void deleteUser() {
        System.out.println(">> 删除一个User <<");
    }

    @Override
    public void queryUsers() {
        System.out.println(">> 查询所有User <<");
    }

    @Override
    public void saveUser() {
        System.out.println(">> 保存一个User <<");
    }

}

通知

public class LogAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        this.pre();
        // 执行原方法
        Object result = methodInvocation.proceed(); 
        this.post();
        return result;
    }
    private void pre(){
        System.out.println("operations pre...");
        
    }
    
    private void post(){
        System.out.println("operations post...");
        
    }
}
public class MainTest {

    // 使用编程的方式实现AOP
    @Test
    public void test() throws Exception {
        UserService userService = new UserServiceImpl();

        // ===================================================

        // 1,声明"切入点",表示要拦截哪些方法
        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
        pointcut.addMethodName("save*");
        pointcut.addMethodName("delete*");

        // 2,声明一个"通知",表示拦截到之后要做什么事
        LogAdvice logAdvice = new LogAdvice();

        // 3,组装为一个切面(切面=切入点+通知)
        Advisor advisor = new DefaultPointcutAdvisor(pointcut, logAdvice);

        // 4,为原对象生成一个代理对象
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisor(advisor); // 添加一个切面
        proxyFactory.setTarget(userService); // 指定目标对象
        userService = (UserService) proxyFactory.getProxy(); // 获取代理对象

        // ===================================================
        // 使用的是代理对象

        userService.saveUser();
        System.out.println();

        userService.deleteUser();
        System.out.println();

        userService.queryUsers();
        System.out.println();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值