一.动态代理模式
(1)产生的代理对象和目标对象实现了共同的接口;(jdk动态代理)
JDK的动态代理 :
1. 用Jdk的API做到的;
2. 代理对象时动态产生的;
注意:
1. 拦截器中invoke方法体的内容就是代理对象方法体的内容;
2. 当客户端执行代理对象,方法的时候,进入到了拦截器的invoke的方法体内;
3. 拦截器invoke方法中的method参数是在调用的时候赋值操作;
cglib产生的代理类是目标类的子类;
springAop工具包下载
(3)示例说明cglib动态代理
1)场景描述
假设你正在进行一个查询系统中薪资的判断,故需要进行日志记录,安全监测,权限判断,后输出查询结果;
UML图:
2)日志记录类
public class Logging {
public void pringlnLog(){
System.out.println("Log 已记录");
}
}
3)安全监测类
public class SafeCheck {
public void safeCheckPrint(){
System.out.println("安全性检测");
}
}
4)权限类
public class AdminCheck {
private String access;
public String getAccess() {
return access;
}
public void setAccess(String access) {
this.access = access;
}
public void adminCheckPrint(){
System.out.println("权限检测");
}
}
5)dao层
public interface SalaryManager {
void selectSalary();
}
实现类:
public class SalaryManagerImp implements SalaryManager {
@Override
public void selectSalary() {
System.out.println("薪资10000");
}
}
6)拦截器
public class SalaryIntercepter implements MethodInterceptor{
//目标类
private Object target;
//日志记录
private Logging logging;
//安全性检测
private SafeCheck safeCheck;
//权限检测
private AdminCheck adminCheck;
public SalaryIntercepter(Object target, Logging logging,
SafeCheck safeCheck, AdminCheck adminCheck) {
super();
this.target = target;
this.logging = logging;
this.safeCheck = safeCheck;
this.adminCheck = adminCheck;
}
public Object createProxy(){
Enhancer enhancer=new Enhancer();
enhancer.setCallback(this);
enhancer.setSuperclass(this.target.getClass());
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
logging.pringlnLog();
safeCheck.safeCheckPrint();
if(adminCheck.getAccess().equals("admin")){
arg1.invoke(target, arg2);
}else{
System.out.println("你没有权限");
}
return null;
}
}
7)测试
@Test
public void testSalaryIntercepter() {
AdminCheck adminCheck = new AdminCheck();
adminCheck.setAccess("admin");
Logging logging = new Logging();
SafeCheck safeCheck = new SafeCheck();
Object target = new SalaryManagerImp();
SalaryIntercepter intercepter = new SalaryIntercepter(target, logging,
safeCheck, adminCheck);
SalaryManager manager = (SalaryManager) intercepter.createProxy();
manager.selectSalary();
}
(4)重构
在这里如果我们想要加上某个功能来监测,故我们进行拦截器重构实现;
基本思路是: 1)提供一个监测接口,使得日志,安全,权限,都实现该接口,使用List进行重构实现;
UML图 :
1)监测接口
public interface Intercepter {
void intercepterCheck();
}
2)日志记录类
public class Logging implements Intercepter{
@Override
public void intercepterCheck() {
System.out.println("Log 已记录");
}
}
3)安全监测类
public class SafeCheck implements Intercepter{
@Override
public void intercepterCheck() {
System.out.println("安全性检测");
}
}
4)dao层不变
5)拦截器实现
public class SalaryIntercepter implements InvocationHandler {
// 目标类
private Object target;
private List<Intercepter> intercepters;
// //日志记录
// private Logging logging;
// //安全性检测
// private SafeCheck safeCheck;
// //权限检测
// private AdminCheck adminCheck;
public SalaryIntercepter(Object target, List<Intercepter> intercepters) {
super();
this.target = target;
this.intercepters = intercepters;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
for (Intercepter intercepter : intercepters) {
intercepter.intercepterCheck();
}
method.invoke(target, args);
return null;
}
}
6).测试
@Test
public void testSalaryIntercepter(){
AdminCheck adminCheck=new AdminCheck();
adminCheck.setAccess("admin");
Logging logging = new Logging();
SafeCheck safeCheck = new SafeCheck();
Object target = new SalaryManagerImp();
List<Intercepter> intercepters=new ArrayList<Intercepter>();
intercepters.add(safeCheck);
intercepters.add(adminCheck);
intercepters.add(logging);
SalaryIntercepter intercepter = new SalaryIntercepter(target, intercepters);
SalaryManager salaryManager = (SalaryManager) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),intercepter);
salaryManager.selectSalary();
}
事务,日志,安全性框架,权限等都是切面,非目标类的都是切面;
切面中的方法就是通知;
(5).织入
好处:事务,日志,安全性框架,权限,目标方法之间完全是松耦合的;
使用:找目标类及其找目标类的切面;
1)当spring容器启动的时候,加载了spring的配置文件;
2)未配置文件中所有的类创建对象;
3)spring容器解析aop:config的配置:解析切入点表达式,用切入点表达式纳入spring容器中的bean匹配;
如果成功,则会位该bean创建代理对象,代理对象的方法=目标方法+通知
4)在客户端利用context.getbean获取对象的时候,如果有代理对象,则返回代理对象;
5)如果目标类没有实现接口,则spring容器采用cglib的方式产生代理对象,否则采用jdk的代理对象;
<aop:config>
<aop:pointcut expression="execution(* cn.labelnet.salary.SalaryManagerImp.*())" id="pointsalary"/>
<aop:aspect ref="logging">
<aop:before method="pringlnLog" pointcut-ref="pointsalary"/>
</aop:aspect>
<aop:aspect ref="adminCheck">
<aop:around method="isAdmin" arg-names="point" pointcut-ref="pointsalary"/>
</aop:aspect>
<aop:aspect ref="safeCheck">
<aop:before method="safeCheckPrint" pointcut-ref="pointsalary"/>
</aop:aspect>
</aop:config>
1)前置通知(aop:before)
在目标方法执行之前执行,无论目标方法是否抛出异常,都执行,因为在执行前置通知的时候,目标方法还没有执行,还没有遇到异常;
<aop:before method="pringlnLog" pointcut-ref="pointsalary"/>
2)后置通知(aop:after-returning)
在目标方法之后执行,当目标方法遇到异常,后置通知不执行;后置通知可以接受目标方法的参数var,但是需要注意,后置通知的参数名称和配置文件中的returning="var"的值是一致的;
3)最终通知(aop:after)
在目标方法执行之后执行,无论目标方法是否跑出异常,都执行,因为相当于finally;
4)异常通知(aop:after-throwing)
接收的目标方法和抛出的异常信息,异常方法中的参数th和配置文件中的throwing="th"一致;
5)环绕通知(aop:)
如果不在环绕通知中调用ProceedingJoinoPoint的proceed,目标方法不会执行;可以控制目标方法的执行;
场景还是上面的查询薪资;
UML 图 :
1)权限检查
public class AdminCheck {
private String access;
public String getAccess() {
return access;
}
public void setAccess(String access) {
this.access = access;
}
//重要 对于配置文件中 aop:round 环绕通知
public void isAdmin(ProceedingJoinPoint point) throws Throwable{
if(this.access.equals("admin")){
point.proceed();
}else{
System.out.println("sorry,you no 权限");
}
}
}
2)日志记录
public class Logging {
public void pringlnLog(){
System.out.println("Log 已记录");
}
}
3)安全监测
public class SafeCheck {
public void safeCheckPrint(){
System.out.println("安全性检测");
}
}
4)dao层实现
接口:
public interface SalaryManager {
void selectSalary();
}
接口实现:
public class SalaryManagerImp implements SalaryManager {
@Override
public void selectSalary() {
System.out.println("薪资10000");
}
}
5)配置实现
在上面我们知道使用 Intercepter 拦截器实现,动态代理,在这里我们使用spring配置文件实现:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="salaryTarget" class="cn.labelnet.salary.SalaryManagerImp"></bean>
<bean id="adminCheck" class="cn.labelnet.salary.AdminCheck">
<property name="access" value="admin"></property>
</bean>
<bean id="logging" class="cn.labelnet.salary.Logging"></bean>
<bean id="safeCheck" class="cn.labelnet.salary.SafeCheck"></bean>
<aop:config>
<aop:pointcut expression="execution(* cn.labelnet.salary.SalaryManagerImp.*())" id="pointsalary"/>
<aop:aspect ref="logging">
<aop:before method="pringlnLog" pointcut-ref="pointsalary"/>
</aop:aspect>
<aop:aspect ref="adminCheck">
<aop:around method="isAdmin" arg-names="point" pointcut-ref="pointsalary"/>
</aop:aspect>
<aop:aspect ref="safeCheck">
<aop:before method="safeCheckPrint" pointcut-ref="pointsalary"/>
</aop:aspect>
</aop:config>
</beans>