代理模式:
代理模式的定义:代理模式(Proxy Pattern)是程序设计中的一种设计模式。为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式的组成:
- 1、抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
- 2、真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
- 3、代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
代理的分类:
- 静态代理:在程序运行之前就已经确定代理类与其代理的真实类。例如,在编写程序时,指定一个接口A。类B实现A接口完善其逻辑代码。类C也实现了A接口,但是其并没有自己的逻辑代码,而是引用了类B的逻辑代码,类C也可以在其引用的类B的逻辑代码的基础上做一些其他操作。此时,就称类C是类B的静态代理。
- 动态代理:在程序运行时才确定代理类,动态代理目前有两种常见的实现:jdk动态代理和cglib动态代理。
Spring AOP中隐含的动态代理:
一:Spring AOP中使用jdk动态代理:
想要在spring中使用jdk代理,需要以下几个条件:
1、定义一个接口,例:
package cn.jingpengchong.math.service;
public interface IMathService {
int div(int a, int b);
}
2、定义一个实现该接口的类,例:
package cn.jingpengchong.math.service;
import org.springframework.stereotype.Service;
//使用spring自动实例化对象,需添加能够被扫描到的特定注解
@Service
public class MathService implements IMathService {
public int div(int a, int b) {
System.out.println("日志:The div method begins");
return a/b;
}
}
3、在spring的xml配置文件中做如下配置:
<!--假如不写proxy-target-class属性则默认是false-->
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
4、定义切面,并且切入点要有与真实类相匹配的增强处理,例:
package cn.jingpengchong.math.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//该注解指定该类为切面类
@Aspect
//使用spring自动实例化对象,需添加能够被扫描到的特定注解
@Component
public class MethodAOP {
//前置处理,表达式要与真实类的某个方法匹配,否则无法生成代理类
@Before("execution(public int cn.jingpengchong.math.service.MathService.*(..))")
public void before() {
//此处可以添加附加操作
}
}
5、通过接口对应的Class类的实例化对象获得代理类,例:
package cn.jingpengchong.math.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.jingpengchong.math.service.IMathService;
import cn.jingpengchong.math.service.MathService;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
//此处不能通过真实类对应的Class类的实例化对象获得代理类
IMathService mathService = applicationContext.getBean(IMathService.class);
System.out.println("mathService的类是:");
System.out.println(mathService.getClass());
System.out.println("mathService的父类是:");
System.out.println(mathService.getClass().getSuperclass());
Class<?>[] interfaces = mathService.getClass().getInterfaces();
System.out.println("mathService的类实现的接口有:");
for (Class<?> c : interfaces) {
System.out.println(c);
}
applicationContext.close();
}
}
运行结果如下,顺便可以观察以下代理类的类、父类及实现的接口:
假如没有第3步所做的配置,则运行结果如下:
假如第4步的切面中没有一个表达式与真实类中任何一个方法匹配,则结果如下:
假如第5步用真实类对应的Class类的实例化对象获得代理类,则结果如下:
二、Spring AOP中使用cglib动态代理:
Spring中使用cglib动态代理与jdk代理类似,不同之处在于:
1、将spring的xml配置文件中的aop:aspectj-autoproxy标签的proxy-target-class属性值改为true;
2、既可以通过接口对应的Class类的实例化对象获得代理类,也可以通过真实类对应的Class类的实例化对象获得代理类。
- 通过两种方式获得代理类的结果是一样的:
三、jdk动态代理和cglib动态代理的区别:
1、jdk动态代理基于接口实现,而cglib动态代理基于真实类实现;
2、jdk动态代理间接实现接口,与真实类没有继承关系,而cglib动态代理则直接继承了真实类。
Spring的事务管理自动使用动态代理:
除了在AOP中我们发现了动态代理的存在,其实在spring的事务管理中也用到了动态代理,Spring会将添加了@Transactional注解的类创建代理类,尽管我们在Spring的xml配置文件中并没有配置aop:aspectj-autoproxy标签。例如在下面的例子中:
package cn.jingpengchong.car.service;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.jingpengchong.coupon.service.ICouponService;
@Service
public class CarService implements ICarService {
@Autowired
private ICouponService couponService;
//购物车购买
@Transactional
@Override
public boolean batch(String userId,Map<String,Integer> commodities) {
Set<Entry<String, Integer>> set = commodities.entrySet();
for (Entry<String, Integer> commodity : set) {
String bookId = commodity.getKey();
int count = commodity.getValue();
System.out.println(bookId+","+count);
couponService.insert(userId,bookId, count);
}
return true;
}
}
并且在spring的xml文件中也没有配置aop:aspectj-autoproxy标签,但是当我们写个测试类如下:
package cn.jingpengchong.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.jingpengchong.coupon.service.ICouponService;
public class Test01 {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
ICouponService couponService = applicationContext.getBean(ICouponService.class);
System.out.println("couponService的类是:");
System.out.println(couponService.getClass());
System.out.println("couponService的父类是:");
System.out.println(couponService.getClass().getSuperclass());
Class<?>[] interfaces = couponService.getClass().getInterfaces();
System.out.println("mathService的类实现的接口有:");
for (Class<?> c : interfaces) {
System.out.println(c);
}
applicationContext.close();
}
}
发现结果还是使用了动态代理,观察代理类的类名我们可以清楚的知道这是一个jdk动态代理,因为其标志性字眼“Proxy”: