代理模式(proxy):是一种设计模式,不改变原代码的情况下增加功能
静态代理:在编写代码的阶段就以及确定要执行代码、指向对象等等
动态代理:在程序运行阶段根据实际的情况执行不同的代码、指向不同对象等等,jdk自带的和cglib实现
静态代理演示
interface PlayBasketball{
public void play();
}
class Ikun implements PlayBasketball{
@Override
public void play() {
System.out.println("ikun最棒");
}
}
class IkunAgent implements PlayBasketball{
private PlayBasketball target;
@Override
public void play() {
System.out.println("ikun说要先谈钱");
target.play();
System.out.println("好的,打完收工,拿钱");
}
public IkunAgent(PlayBasketball target) {
super();
this.target = target;
}
}
public class Demo {
public static void main(String[] args) {
IkunAgent proxy = new IkunAgent(new Ikun());
proxy.play();
/*控制台的输出结果如下:
ikun说要先谈钱
ikun最棒
好的,打完收工,拿钱
*/
}
}
由代码可以看出,这静态代理和web开发中service、dao层的逻辑关系完全一样。
动态代理中的JDK代理:是javaJDK自带的一种代理方式
interface BookDao{
public void addBook();
}
class BookDaoImpl implements BookDao{
@Override
public void addBook() {
System.out.println("执行SQL");
}
}
class ProxyFactory{
private Object target;
public ProxyFactory(Object target) {//这样写可以代理很多东西
super();
this.target = target;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(
ProxyFactory.class.getClassLoader(), //取类加载器,任何类都可以去取,因为类加载器只有一个
target.getClass().getInterfaces(), //获得接口
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
Object result = method.invoke(target, args);
System.out.println("提交事务");
return result;
}
});
}
}
public class Demo2 {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new BookDaoImpl()); //动态代理,这里参数带谁就代理谁
Object object = factory.getProxyInstance(); //调用factory的方法
if(object instanceof BookDao) {
BookDao bookDao=(BookDao)object;
bookDao.addBook();
}
}
}
动态代理之cglib代理,需要导包
class StudentDao{
public void addStu() {
System.out.println("执行SQL操作数据库");
}
}
//创建工厂类:用来生成代理对象的,实现MethodInterceptor接口:拦截方法的执行,
//只要调用指定的方法就会被拦截
class CglibProxyFactory implements MethodInterceptor{
//维护被代理对象
private Object target;
public CglibProxyFactory(Object target) {
super();
this.target = target;
}
//生成代理对象
public Object getProxyInstance() {
//1.创建工具类对象:生成代理对象
Enhancer enhancer = new Enhancer();
//2.设置父类:设置代理类的父类
enhancer.setSuperclass(target.getClass());
//3.设置回调函数
enhancer.setCallback(this);//拦截方法之后到哪里去找intercept()方法
//4.创建子类对象
Object object = enhancer.create();
return object;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("开启事务....");
//执行被代理对象原有的功能
Object result = method.invoke(target, args);
System.out.println("提交事务....");
return result;
}
}
public class Demo3 {
public static void main(String[] args) {
CglibProxyFactory factory = new CglibProxyFactory(new StudentDao());
Object object = factory.getProxyInstance();
if (object instanceof StudentDao) {
System.out.println("yes");
StudentDao studentDao = (StudentDao)object;
studentDao.addStu();
}
}
}
特别注意
被代理类在有实现接口的时候一般用jdk代理,而在被代理类没有实现任何接口时可以使用cglib代理,cglib是通过导包实现的,并且cglib生成的代理类是被代理类的子类
重要扩展
AOP面向切面由正是由动态代理实现的,如果被代理对象有实现接口,默认JDK代理生成代理类,在获取代理对象时应该用接口接收对象,如果代理类没有实现接口,会采用cglib代理的方法,生成代理类、代理对象,应该用父类去接收代理对象。即如下:
BankService bankService = (BankService)ac.getBean("bankService");//有接口
Bank bank = (Bank)ac.getBean("bank"); //无接口
<!--引入约束文件-->
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 2.配置切面:先导入aop的命名空间、约束文件 -->
<aop:config>
<!-- 配置切点:通过切入点表达式找到对应的方法 -->
<aop:pointcut expression="execution(* com.woniuxy.test.BankServiceImpl.sendMony())" id="pc"/>
<!-- 给切点方法添加通知 -->
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="pc"/>
<aop:advisor advice-ref="afterAdvice" pointcut-ref="pc"/>
</aop:config>
<bean id="beforeAdvice" class="com.woniuxy.test.BeforeAdvice"></bean>
<bean id="afterAdvice" class="com.woniuxy.test.MyAfterAdvice"></bean>
声明式事务:上面这种切法是schema-base方式,每一个通知都要编写对应的类,其实不好用
更多的是使用aspectj方式,只需要编写一个切面类,就可以实现很多个方法,作为不同的通知,如下:
public class Advice {
public void before() {
System.out.println("开启事务........");
}
public void after() {
System.out.println("提交事务........");
}
public void aferReturn() {
System.out.println("返回后通知");
}
public void around() {
System.out.println("环绕通知");
}
public void exception() {
System.out.println("异常通知:回滚");
}
}
xml的配置方式和schema差不多
<!-- aspectj方式 -->
<!-- 1.配置切面类对象 -->
<bean id="advices" class="com.woniuxy.advice.Advice"></bean>
<!-- 2.配置切面 -->
<aop:config>
<!-- 配置切点:给哪些方法添加通知 -->
<aop:pointcut expression="execution(* com.woniuxy.test.BankServiceImpl.sendMoney())"
id="pc"/>
<!-- 给切点配置通知
ref:引用通知类对象,说明通知在该类里面
-->
<aop:aspect ref="advices">
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="pc"/>
<!-- 后置 -->
<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
<!-- 4.service -->
<bean id="bankService" class="com.woniuxy.test.BankServiceImpl"></bean>
所以我们一般采用声明式事务,当然他也是可以用注解的。
@Component
@Aspect //将当前类设置为切面类
public class Advice {
@Pointcut("execution(* com.woniuxy.test.BankServiceImpl.sendMoney())")
public void pointCut() {
}
@Before("pointCut()")
public void before() {
System.out.println("开启事务........");
}
@After("pointCut()")
public void after() {
System.out.println("提交事务........");
}
@AfterReturning("pointCut()")
public void aferReturn() {
System.out.println("返回后通知");
}
@Around("pointCut()")
public void around() {
System.out.println("环绕通知");
}
@AfterThrowing("pointCut()")
public void exception() {
System.out.println("异常通知:回滚");
}
}
aop的扫描(切面编程)和id的扫描(IOC)
<!-- 开启aop的注解扫描 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 配置扫描 -->
<context:component-scan base-package="com.woniuxy.*"></context:component-scan>