首先先看java中的静态代理,一个接口,一个实现类,一个代理类,还有一个测试类
public interface UserService {
public boolean checkLogin(String account, String password);
}
public class UserServiceImpl implements UserService {
@Override
public boolean checkLogin(String account, String password) {
if("admin".equals(account) && "1".equals(password)) {
return true;
} else {
return false;
}
}
}
public class UserServiceProxy implements UserService {
private UserService service;
public UserServiceProxy(UserService service) {
this.service = service;
}
@Override
public boolean checkLogin(String account, String password) {
System.out.println("业务处理前");
System.out.println("password:" + password);
System.out.println("account:" + account);
boolean result = false;
//代理模式有权限控制调用方法,例如判断登录的用户,赋予不同的权限
if("root".equals(account) && "1".equals(password)) {
result = true;
} else {
result = service.checkLogin(account, password);
}
System.out.println("业务处理后:" + result);
return result;
}
}
//静态代理
// UserService service = new UserServiceImpl();
// UserService serviceProxy = new UserServiceProxy(service);
// serviceProxy.checkLogin("admin","1");
实现效果如下
其中代理类聚合了实现类,代理模式其实和装饰类模式差不多,
区别:代理模式有权限控制是否调用真实的目标方法
装饰模式必须调用目标方法
接下来是动态代理的代码,动态代理是新建一个动态代理的类,实现了InvocationHandler接口,代码如下
public class UserServiceInvocation implements InvocationHandler {
private UserService service;
public UserServiceInvocation(UserService service) {
this.service = service;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("业务处理前");
System.out.println(proxy.getClass().getName());
Object result = method.invoke(this.service, args);
System.out.println("业务处理后:" + result);
return result;
}
}
测试类的代码如下
//动态代理
// UserService service = new UserServiceImpl();
// UserServiceInvocation invocation = new UserServiceInvocation(service);
// UserService proxy = (UserService)Proxy.newProxyInstance(service.getClass().getClassLoader(),
// service.getClass().getInterfaces(), invocation);
// System.out.println(proxy.checkLogin("admin", "1"));
//使用动态代理返回
return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//如果调用的是close方法,则将数据库连接对象重新加入到集合中
if("close".equals(method.getName())) {
connList.add(conn);
System.out.println("数据库对象已回收:" + conn);
System.out.println("连接池的个数:" + connList.size());
System.out.println("----------------------------------------------------");
return null;
} else {
return method.invoke(conn, args);
}
}
});
这是连接池的例子,将在下一篇文章介绍
下面是spring框架的动态代理实现,首先是创建一个动态代理的类,这个类也是用来打印日志的,其中的ProceedingJoinPoint参数是切面,通过他可以获得目标对象的类和方法等
public class LogAspects {
public Object log(ProceedingJoinPoint point) {
try {
System.out.println("业务处理前");
System.out.println(point.getTarget().getClass().getName());
for(Object arg : point.getArgs()) {
System.out.println(arg);
}
//调用目标对象方法
Object result = point.proceed();
System.out.println("业务处理后:" + result);
return result;
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
}
在application.xml配置文件中配置
<context:component-scan base-package="user,aop"></context:component-scan>//这段代码是指需要扫描的包
<bean id="logAspects" class="aop.LogAspects"></bean>
<aop:config>
<aop:pointcut expression="execution(* aop.*.*(..))" id="logpointcut"/>
<aop:aspect ref="logAspects">
<aop:around method="log" pointcut-ref="logpointcut"/>
</aop:aspect>
</aop:config>
<aop:advisor> 定义一个AOP通知者
<aop:after> 后通知
<aop:after-returning> 返回后通知
<aop:after-throwing> 抛出后通知
<aop:around> 周围通知
<aop:aspect>定义一个切面
<aop:before>前通知
<aop:config>顶级配置元素,类似于<beans>这种东西
<aop:pointcut>定义一个切点
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
除了返回类型模式(上面代码片断中的ret-type-pattern),名字模式和参数模式以外,所有的部分都是可选的。 返回类型模式决定了方法的返回类型必须依次匹配一个连接点。 你会使用的最频繁的返回类型模式是 *,它代表了匹配任意的返回类型。 一个全称限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。 你可以使用 * 通配符作为所有或者部分命名模式。 参数模式稍微有点复杂:() 匹配了一个不接受任何参数的方法, 而 (..) 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 (*) 匹配了一个接受一个任何类型的参数的方法。 模式 (*,String) 匹配了一个接受两个参数的方法,第一个可以是任意类型,第二个则必须是String类型。
下面给出一些常见切入点表达式的例子。
任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
定义在service包或者子包里的任意方法的执行:
execution(* com.xyz.service..*.*(..))
在service包里的任意连接点(在Spring AOP中只是方法执行) :
within(com.xyz.service.*)
在service包或者子包里的任意连接点(在Spring AOP中只是方法执行) :
within(com.xyz.service..*)
实现了 AccountService 接口的代理对象的任意连接点(在Spring AOP中只是方法执行) :
this(com.xyz.service.AccountService)
'this'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得代理对象可以在通知体内访问到的部分。
实现了 AccountService 接口的目标对象的任意连接点(在Spring AOP中只是方法执行) :
target(com.xyz.service.AccountService)
'target'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得目标对象可以在通知体内访问到的部分。
任何一个只接受一个参数,且在运行时传入的参数实现了 Serializable 接口的连接点 (在Spring AOP中只是方法执行)
args(java.io.Serializable)
'args'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得方法参数可以在通知体内访问到的部分。 请注意在例子中给出的切入点不同于 execution(* *(java.io.Serializable)): args只有在动态运行时候传入参数是可序列化的(Serializable)才匹配,而execution 在传入参数的签名声明的类型实现了 Serializable 接口时候匹配。
有一个 @Transactional 注解的目标对象中的任意连接点(在Spring AOP中只是方法执行)
@target(org.springframework.transaction.annotation.Transactional)
'@target' 也可以在binding form中使用:请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。
任何一个目标对象声明的类型有一个 @Transactional 注解的连接点(在Spring AOP中只是方法执行)
@within(org.springframework.transaction.annotation.Transactional)
'@within'也可以在binding form中使用:- 请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。
任何一个执行的方法有一个 @Transactional annotation的连接点(在Spring AOP中只是方法执行)
@annotation(org.springframework.transaction.annotation.Transactional)
'@annotation' 也可以在binding form中使用:- 请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。
任何一个接受一个参数,并且传入的参数在运行时的类型实现了 @Classified annotation的连接点(在Spring AOP中只是方法执行)
@args(com.xyz.security.Classified)
spring的动态代理的标签虽然不是很懂,不过感觉需要用到时能调用就可以了