学习Spring的知识,需要学习的是IOC思想和AOP思想,之前学习了IOC,就是控制反转,在IOC中也可以进行依赖注入的方式
学习控制反转就是学习在容器对一个对象的创建。
在学习AOP切面编程,所谓aop就是类似过滤器和拦截器的思想,实现对一些业务代码、服务代码的分离,用来降低服务代码和业务代码的耦合性
实现AOP的方式主要采用的是代理模式,分为:静态代理模式、动态代理模式和cglib子类代理模式
①。静态代理:
采用静态代理的特点:
必须实现和被代理类相同的接口,绑定指定的接口,扩展性和通用性不好
②动态代理模式:
采用动态代理的特点:
可以生成任何接口的代理类,通用性比较好,但是也是必须依赖接口,没有接口也是不能使用动态代理
③Cglib子类代理
特点是代理类无需实现任何的接口
而使用Spring的AOP,所需要的是设置四个元素:
JoinPoint:连接点,一般为目标对象的每个方法
PontCut:切入点,切入了服务代码的连接点
Advice:通知:在切入点上面需要切入的服务代码
Aspect:切面:在指定的切入点切入指定的通知形成切面
在applicationContext.xml中的切面配置通知的类型:前置通知、后置通知和环绕通知
①前置通知(在目标对象的连接点之前就调用切面类)
②后置通知(在目标对象的连接点之后就调用切面类)
③环绕通知(在目标对象的连接点之前和之后就调用切面类)
基本的aop主要是用来比如整合持久层的时候,采用来切面事务管理
学习控制反转就是学习在容器对一个对象的创建。
在学习AOP切面编程,所谓aop就是类似过滤器和拦截器的思想,实现对一些业务代码、服务代码的分离,用来降低服务代码和业务代码的耦合性
实现AOP的方式主要采用的是代理模式,分为:静态代理模式、动态代理模式和cglib子类代理模式
①。静态代理:
/**
* 代理类: 用于切入服务代码(事务代码)
* @author APPle
*
*/
public class UserDaoProxyImpl implements IUserDao {
private IUserDao userDao;
//获取或创建被代理类对象
public UserDaoProxyImpl(IUserDao userDao){
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("开启事务");
//调用核心业务方法
userDao.save();
System.out.println("提交事务");
}
}
采用静态代理的特点:
必须实现和被代理类相同的接口,绑定指定的接口,扩展性和通用性不好
②动态代理模式:
/**
* 用于生成代理类的工厂类
* @author APPle
*
*/
public class ProxyBeanFactory {
private Object target;
/**
* 绑定被代理类对象
* target: 需要生成代理类对象的被代理
*/
public void bind(Object target){
this.target = target;
}
/**
* 获取需要生成的代理类对象
*/
public Object getProxy(){
/**
* jdk动态代理:利用jdk的工具类生成代理类对象
* java.lang.reflect.Proxy类:
*
* static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
*/
Object proxy =
Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器(没有特定要求)
target.getClass().getInterfaces(), //需要指定实现的代理接口(通常和被代理类相同的接口)
new InvocationHandler(){// 代理完之后需要做什么??(需要提供InvocationHandler的实现类)
//invoke: 代理类调用的每个方法
/**
* proxy: 代理类对象
* method: 方法对象
* args: 方法传入的参数
* 返回值:方法调用后返回的值
*/
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//System.out.println("调用了代理类的方法:"+method.getName());
System.out.println("开启事务");
//调用被代理类对象的核心业务代码
Object result = method.invoke(target, args);
System.out.println("提交事务");
return result;
}
}
);
return proxy;
}
}
采用动态代理的特点:
可以生成任何接口的代理类,通用性比较好,但是也是必须依赖接口,没有接口也是不能使用动态代理
③Cglib子类代理
/**
* 用于生成子类的代理类的工厂类
* @author APPle
*
*/
public class ProxyBeanFactory{
private Object target;
/**
* 绑定被代理类对象
* target: 需要生成代理类对象的被代理
*/
public void bind(Object target){
this.target = target;
}
/**
* 获取需要生成的代理类对象
*/
public Object getProxy(){
//1.创建工具类
Enhancer enhancer = new Enhancer();
//2.*指定父类(被代理类对象的类型)
enhancer.setSuperclass(target.getClass());
//3.指定回调函数(代理完后需要做什么?)
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy arg3) throws Throwable {
System.out.println("开启事务");
//调用目标对象的核心业务方法
Object result = method.invoke(target, args);
System.out.println("结束事务");
return result;
}
});
//4.生成子类代理对象
return enhancer.create();
}
}
特点是代理类无需实现任何的接口
而使用Spring的AOP,所需要的是设置四个元素:
JoinPoint:连接点,一般为目标对象的每个方法
PontCut:切入点,切入了服务代码的连接点
Advice:通知:在切入点上面需要切入的服务代码
Aspect:切面:在指定的切入点切入指定的通知形成切面
package gz.itcast.dao;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 定义切面类
* @author APPle
*
*/
public class MyAspect {
/**
* 常用的通知类:
* 前置通知: 在目标对象的连接点之前调用
* 后置通知:在目标对象的连接点之后调用
* 环绕通知: 在目标对象的连接点之前与之后调用
*/
//定义前置通知
public void before(){
System.out.println("开启事务");
}
//定义后置通知
public void after(){
System.out.println("提交事务");
}
//环绕通知
public void round(ProceedingJoinPoint jp) throws Throwable{
System.out.println("环绕通知前面代码");
//执行目标对象的核心方法
jp.proceed();//执行连接点方法
System.out.println("环绕通知后面代码");
}
}
<!-- 1.创建目标对象 -->
<bean id="userDao" class="gz.itcast.dao.UserDao"></bean>
<!-- 2.创建切面类对象 -->
<bean id="myaspect" class="gz.itcast.dao.MyAspect"></bean>
<!-- 3.配置切面 -->
<aop:config>
<!-- 切面
ref: 关联切面类对象
-->
<aop:aspect ref="myaspect">
<!-- 3.1 切入点
id: 切入点的别名
expression: 切入点表达式 :格式: execution(修饰符 方法的返回值类型 方法名(包+类)(方法中参数列表))
-->
<aop:pointcut expression="execution(* save*(..))" id="mypt"/>
<!-- 3.2 通知 -->
<!-- 前置通知
method: 切面类中的前置通知的方法名称
pointcut-ref: 需要关联的切入点
-->
<aop:before method="before" pointcut-ref="mypt"/>
<aop:after method="after" pointcut-ref="mypt"/>
<aop:around method="round" pointcut-ref="mypt"/>
</aop:aspect>
</aop:config>
<!-- 切换spring底层代理类生成机制
false : 有接口的情况下使用jdk动态代理
true: 强制使用cglib生成代理类对象
-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
在applicationContext.xml中的切面配置通知的类型:前置通知、后置通知和环绕通知
①前置通知(在目标对象的连接点之前就调用切面类)
<aop:before method="before" pointcut-ref="mypt"/>
②后置通知(在目标对象的连接点之后就调用切面类)
<aop:after method="after" pointcut-ref="mypt"/>
③环绕通知(在目标对象的连接点之前和之后就调用切面类)
//环绕通知
public void round(ProceedingJoinPoint jp) throws Throwable{
System.out.println("环绕通知前面代码");
//执行目标对象的核心方法
jp.proceed();//执行连接点方法
System.out.println("环绕通知后面代码");
}
<aop:around method="round" pointcut-ref="mypt"/>
基本的aop主要是用来比如整合持久层的时候,采用来切面事务管理