代理
我们拿事务的实现来举例。首先,我们模拟一下事务,编写一个类
public class TransactionManager {
public void begin() {
System.out.println("开启事务");
}
public void commit() {
System.out.println("提交事务");
}
public void rollback() {
System.out.println("回滚事务");
}
}
没有代理
没有代理的事务实现方式 -装饰模式
可以在service实现类中开启事务,代码类似于如下
public class EmployeeServiceImplWapper implements IEmployeeService {
private EmployeeServiceImpl service;
private TransactionManager txManager = new TransactionManager();
public EmployeeServiceImplWapper(EmployeeServiceImpl service) {
this.service = service;
}
@Override
public void add() {
try {
txManager.begin();
service.add();
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
}
}
在每次增加一个方法的时候,都要添加事务,显得比较麻烦。基于责任分离的原则,service层的责任就是关注于逻辑,代码应当只关注于逻辑,只编写逻辑代码。
在调用service方法的时候,使用如下代理
public class App {
@Test
public void testExtends() throws Exception {
EmployeeServiceImpl empSer = new EmployeeServiceImpl();
IEmployeeService serviceWapper = new EmployeeServiceImplWapper(empSer);
serviceWapper.add();
}
}
至此,没有使用代理的事务实现方式的完成
这种方式是不安全的。因为调用者完全可以跳过EmployeeServiceImplWapper类,直接调用EmployeeServiceImpl的add方法,这样调用的方法就没有事务了。
代理的事务实现方式
静态代理
@Component
public class EmployeeServiceImplWapper implements IEmployeeService {
private EmployeeServiceImpl service;
private TransactionManager txManager = new TransactionManager();
public void setService(EmployeeServiceImpl service) {
this.service = service;
}
@Override
public void add() {
try {
txManager.begin();
service.add();
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="ser" class="com.test.EmployeeServiceImplWapper">
<property name="service">
<bean class="com.test.service.impl.EmployeeServiceImpl"></bean>
</property>
</bean>
</beans>
上述使用了spring的管理工具,service不再暴露在外。
静态代理的优点:
- 业务类只需要关注业务逻辑本身,保证了业务类的重用性。
静态代理的缺点:
- 代理对象的某个接口只服务于某一种类型的对象,也就是说每一个真实对象都得创建一个代理对象。
- 如果需要代理的方法很多,则要为每一种方法都进行代理处理。
- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
动态代理
@Component
public class TransactionManagerInvocationHandler implements InvocationHandler {
@Resource
private TransactionManager txManager;
@Resource
private EmployeeServiceImpl service;
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj = null;
try {
txManager.begin();
obj = method.invoke(service, args);
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
return obj;
}
public T getProxy() {
Object proxy = (IEmployeeService) Proxy.newProxyInstance(Thread
.currentThread().getContextClassLoader(),
new Class[] { IEmployeeService.class }, this);
return (T) proxy;
}
}
动态代理类是在程序运行期间由JVM通过反射等机制动态的生成的,所以不存在代理类的字节码文件。代理对象和真实对象的关系是在程序运行时候才确定的。
接口InvocationHandler的invoke方法简介:
Object proxy --> 生成的代理对象
Method method --> 代理对象调用的方法
Object[] args --> 代理对象调用的方法的参数
return obj --> 调用方法Method method返回的值
jdk动态代理总结:
JAVA动态代理是使用java.lang.reflect包中的Proxy类与InvocationHandler接口这两个来完成的。
要使用JDK动态代理,必须要定义接口。
JDK动态代理将会拦截所有pubic的方法(因为只能调用接口中定义的方法),这样即使在接口中增加了新的方法,不用修改代码也会被拦截。
如果只想拦截一部分方法,可以在invoke方法中对要执行的方法名进行判断。
第三方代理CGLIB
@Component
public class TransactionManagerEnhancerHandler implements InvocationHandler {
@Resource
private TransactionManager txManager;
@Resource
private EmployeeServiceImpl service;
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj = null;
try {
txManager.begin();
obj = method.invoke(service, args);
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
}
return obj;
}
public T getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(EmployeeServiceImpl.class);
enhancer.setCallback(this);
return (T) enhancer.create();
}
}
cglib对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
CGLIB动态代理总结:
- CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
- 要求类不能是final的,要拦截的方法要是非final、非static、非private的。
- 动态代理的最小单位是类(所有类中的方法都会被处理)。
spring对代理的选择
当代理的类没有实现接口的时候,使用的代理方式是CGLIB。如果代理的类有实现的接口的时候,使用的代理方式就是jdk动态代理