Spring学习记录(二)
1.Spring中的AOP思想
aop思想:横向重复,纵向抽取。
AOP(Aspect-OrientedProgramming,面向切面编程),AOP包括切面(Aspect),通知(Advice),连接点(joinpoint),
实现方式就是通过目标对象的代理在连接点前后加入通知,完成统一的切面操作。
这个思想在filter过滤器,拦截器,动态代理中就已经使用过,例如:
(1) 在解决乱码的问题上
(2) 在动态代理上,管理事务:(Proxy.newProxyInstance(Classloader cloader,Interface[] arr, InvocationHandle h))
(3) 拦截器中在参数赋值上,一般每个action的参数赋值,都需要有这个参数,但是有拦截器可以完成把参数赋值到action中,或者校验登录
2.AOP实现原理
Spring能够为容器中管理的对象生成动态代理对象。
以前未使用Spring时,要使用动态代理,我们需要自己调用这个方法:
Proxy.newProxyInstance(Classloader cloader,Interface[] arr, InvocationHandle h),生成代理对象。
使用Spring后,能帮助我们生成代理对象(只需要在配置文件中配置,或者注解配置即可),那么为什么与AOP思想有关呢?
比如上面例子中的管理事务,使用动态代理对象在进行事务的管理,只需要写一次代码就能够完成,但是需要自己手写代理对象,而用Spring可以自动的生成Service的代理对象,所以称为Spring的AOP思想
所以根据上面的铺垫,AOP的实现原理可以说就是动态代理,或者cglib代理,还有静态织入(引入特定的语法创建“方面”,从而是的编译器可以在编译期间织入有关“方面”的代码)。 ()
(1) 动态代理(优先):被代理对象必须要实现接口,才能产生代理对象,如果没有借口将不能使用动态代理技术
(2) cglib代理(没有接口):第三方代理技术,cglib代理可以对任何类生成代理,代理的原理是对目标对象进行继承代理。如果目标对象被final修饰,那么该类无法被cglib代理。
使用动态代理的一个代码示例:
首先创建接口,以及实现类
/**
* Created by YuKai Fan on 2018/9/27.
*/
public interface UserService {
void save();
void delete();
void update();
void find();
}
/**
* Created by YuKai Fan on 2018/9/27.
*/
public class UserServiceImpl implements UserService {
//这些方法都是业务逻辑,一般来说在进行业务方法进行事务管理
@Override
public void save() {
//如果不使用动态代理每个方法都需要进行打开事务,提交事务的操作
//System.out.println("打开事务");
System.out.println("保存用户");
//System.out.println("提交事务");
} @Override
public void delete() {
System.out.println("删除用户");
} @Override
public void update() {
System.out.println("修改用户");
} @Override
public void find() {
System.out.println("查找用户");
}
}
再创建一个代理工厂,来生成上面接口的一个代理对象
/**
* 用户service层的代理工厂
* Created by YuKai Fan on 2018/9/27.
*/
public class UserServiceProxyFactory implements InvocationHandler{ private UserService us; public UserServiceProxyFactory(UserService us) {
super();
this.us = us;
} public UserService getUserServiceProxy() {
//生成动态代理
UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(), this);//第三个参数,必须要实现InvocationHandler接口 return usProxy;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//使用动态代理,只需要写一次即可
System.out.println("打开事务");
Object invoke = method.invoke(us, args);//方法的执行,需要方法所存在的对象,所以需要有实例。创建一个构造方法,来传递一个执行方法所对应的实例对象
System.out.println("提交事务");
return invoke;
}
}
测试:
/**
* Created by YuKai Fan on 2018/9/27.
*/
public class Demo {
public static void main(String[] args) {
UserService us = new UserServiceImpl();
UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
UserService usProxy = factory.getUserServiceProxy();
usProxy.save();
}
}
测试结果:代理对象每次在执行业务逻辑代码时,都需要执行事务的操作,因为在代理对象在执行方法时,先进行了事务操作。
使用cglib代理的一个代码示例(了解):
创建代理工厂
/**
* cglib代理(了解)
* Created by YuKai Fan on 2018/9/27.
*/
public class UserServiceProxyFactory2 implements MethodInterceptor{ public UserService getUserServiceProxy() {
//生成代理对象
Enhancer en = new Enhancer();
en.setSuperclass(UserServiceImpl.class);//设置对谁进行代理
en.setCallback(this);//代理要做什么
UserService us = (UserService) en.create();//创建代理对象
return us;
} @Override
//参数解析:methodProxy方法代理,method被代理的方法,o代理的原始对象,arg运行期的参数
public Object intercept(Object o, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
//打开事务
System.out.println("打开事务");
//调用原有方法
Object returnValue = methodProxy.invokeSuper(o, arg);
//提交事务
System.out.println("提交事务");
return returnValue;
}
}
测试:
/**
* Created by YuKai Fan on 2018/9/27.
*/
public class Demo {
public static void main(String[] args) {
UserService us = new UserServiceImpl();
UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
UserServiceProxyFactory2 factory2 = new UserServiceProxyFactory2();//cglib代理
UserService usProxy = factory.getUserServiceProxy();
UserService usProxy2 = factory2.getUserServiceProxy();//cglib生成的代理对象
usProxy.save();
usProxy2.delete(); //判断代理对象是否输入被代理对象类型
//只是代理对象与被代理对象实现了相同的接口,但是没有继承关系
System.out.println(usProxy instanceof UserServiceImpl);//false
//而cglib代理,是与被代理对象进行继承,所以类型相同
System.out.println(usProxy2 instanceof UserServiceImpl);//true
}
}
测试结果:
3.AOP名词解析
以上面的源代码为例
1.Joinpoint(连接点):目标对象中,所有可以增强的方法。(联系上面的例子,就是指那些实现接口的实现类的所有业务方法,即save(),delete(),update(),find())
2.Pointcut(切入点):目标对象,已经增强的方法。(创建代理对象后,进行增强的方法。比如在执行方法前开始事务,执行方法后提交事务。在上面的例子中,由于没有指定使用哪一个的特定方法,所以那四个方法全是切入点)
3.Advice(通知/增强):增强的代码。(根据上面的例子,我们需要在执行目标方法前后,打开事务和提交事务,所以那些打开事务,提交事务的代码,就叫做通知)
4.Target(目标对象):被代理对象(实现类,上面的UserServiceImpl)
5.weaving(织入):将通知应用到切入点的过程(将打开事务和提交事务,应用到目标方法的过程)
6.Proxy*代理):将通知应用到目标对象之后,形成代理对象
7.aspect(切面):切入点+通知
下面是Spring AOP的应用示例:
步骤:
(1) 导包
(2) 准备目标对象
/** 目标对象
* Created by YuKai Fan on 2018/9/27.
*/
public class UserServiceImpl implements UserService {
//这些方法都是业务逻辑,一般来说在进行业务方法进行事务管理
@Override
public void save() {
//如果不使用动态代理每个方法都需要进行打开事务,提交事务的操作
//System.out.println("打开事务");
System.out.println("保存用户");
//System.out.println("提交事务");
} @Override
public void delete() {
System.out.println("删除用户");
} @Override
public void update() {
System.out.println("修改用户");
} @Override
public void find() {
System.out.println("查找用户");
}
}
(3) 准备通知
/**
* aop的通知类
* Created by YuKai Fan on 2018/9/28.
*/
public class MyAdvice { //前置通知:目标方法运行前调用
//后置通知(如果出现异常不会调用):在目标方法之后调用
//环绕通知:在目标方法之前和之后都调用
//异常拦截通知:如果出现异常,就会调用
//后置通知(无论是否出现异常都会通知):在目标方法之后调用 //前置通知
public void before() {
System.out.println("这是前置通知!!");
}
//后置通知(如果出现异常不会调用)
public void afterReturning() {
System.out.println("这是后置通知!!(如果出现异常不会调用)");
}
//环绕通知,必须要手动的调用目标方法,所以需要有一个ProceedingJoinPoint对象
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("这是环绕通知之前!!");
Object proceed = pjp.proceed();//调用目标方法,
System.out.println("这是环绕通知之后!!");
return proceed;
}
//异常通知
public void afterException() {
System.out.println("出事了,出现异常了!!");
}
//后置通知(无论是否出现异常都会通知)
public void after() {
System.out.println("这是后置通知(无论是否出现异常都会通知)!!");
} }
(4) 配置进行织入,将通知应用到目标对象中
applicationContext.xml
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd "> <!-- 准备工作:导入aop(约束)命名空间 -->
<!-- 1.配置目标对象 -->
<bean name="userService" class="com.javaweb.service.UserServiceImpl"></bean>
<!-- 2.配置通知对象 -->
<bean name="myAdvice" class="com.javaweb.aspect.MyAdvice"></bean>
<!-- 配置将通知织入目标对象 -->
<aop:config>
<!-- 配置切入点
public void com.javaweb.service.UserServiceImpl.save(),只针对save方法进行织入
void com.javaweb.service.UserServiceImpl.save() public可以省略,默认就是public
* com.javaweb.service.UserServiceImpl.save() 对返回值不做要求
* com.javaweb.service.UserServiceImpl.*() 对目标类下的所有空参方法织入
* com.javaweb.service.UserServiceImpl.*(..) 对方法的参数不做任何要求
* com.javaweb.service.*ServiceImpl.*(..) 可以对任何在service包下以ServiceImpl结尾的类进行织入
* com.javaweb.service..*ServiceImpl.*(..) 可以对service下的子包也进行织入
-->
<aop:pointcut id="pc" expression="execution(* com.javaweb.service.*ServiceImpl.*(..))" />
<aop:aspect ref="myAdvice">
<!-- 指定名为before方法作为前置通知 -->
<aop:before method="before" pointcut-ref="pc"/>
<!-- 后置(无论是否出现异常) -->
<aop:after method="after" pointcut-ref="pc"/>
<!-- 环绕 -->
<aop:around method="around" pointcut-ref="pc"/>
<!-- 异常 -->
<aop:after-throwing method="afterException" pointcut-ref="pc"/>
<!-- 后置(出现异常不会调用) -->
<aop:after-returning method="afterReturning" pointcut-ref="pc" />
</aop:aspect> </aop:config>
</beans>
demo测试:
/**
* Created by YuKai Fan on 2018/9/28.
*/
@ContextConfiguration("classpath:com/javaweb/springAOP/applicationContext.xml")
public class Demo {
@Resource(name="userService")//已经获取到代理对象
private UserService us; @Test
public void fun() {
us.save();
} }
测试结果:
Spring学习记录(二)的更多相关文章
- Spring学习记录(二)---容器和bean属性配置
下载spring包,在eclipse搭建spring环境. 这步我在eclipse中无法导入包,看网上的: http://sishuok.(和谐)com/forum/blogPost/list/242 ...
- Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客
==他的博客应该不错,没有细看 Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客 http://blog.csdn.net/u012706811/article/det ...
- 我的Spring学习记录(二)
本篇就简单的说一下Bean的装配和AOP 本篇的项目是在上一篇我的Spring学习记录(一) 中项目的基础上进行开发的 1. 使用setter方法和构造方法装配Bean 1.1 前期准备 使用sett ...
- 我的Spring学习记录(四)
虽然Spring管理这我们的Bean很方便,但是,我们需要使用xml配置大量的Bean信息,告诉Spring我们要干嘛,这还是挺烦的,毕竟当我们的Bean随之增多的话,xml的各种配置会让人很头疼. ...
- Material Calendar View 学习记录(二)
Material Calendar View 学习记录(二) github link: material-calendarview; 在学习记录一中简单翻译了该开源项目的README.md文档.接下来 ...
- 我的Spring学习记录(五)
在我的Spring学习记录(四)中使用了注解的方式对前面三篇做了总结.而这次,使用了用户登录及注册来对于本人前面四篇做一个应用案例,希望通过这个来对于我们的Spring的使用有一定的了解. 1. 程序 ...
- Spring 学习记录3 ConversionService
ConversionService与Environment的关系 通过之前的学习(Spring 学习记录2 Environment),我已经Environment主要是负责解析properties和p ...
- Spring 学习记录8 初识XmlWebApplicationContext(2)
主题 接上文Spring 学习记录7 初识XmlWebApplicationContext refresh方法 refresh方法是定义在父类AbstractApplicationContext中的. ...
- Spring 学习记录6 BeanFactory(2)
主题 除了Spring 学习记录5 BeanFactory 里写的几个接口外,BeanFactory的实现类还实现了一些其他接口,这篇文章主要介绍这些接口和实现类. 结构 DefaultListabl ...
随机推荐
- AT2044 Teleporter
传送门 这个是真的简单题,随便手玩一下就可以发现最优策略一定是给\(1\)加上自环 然后就可以dfs一下看哪些点子树里深度最深的点到当前点的距离会等于\(k-1\),然后将当前点连向\(1\)(当然特 ...
- EIGRP-3-EIGRP的多参数度量
带宽度量参数本身无法区分10Gbit/s及更高速率的接口.对1Gbit/s接口,默认延迟度量参数已设置为最低值1(10微妙).而且EIGRP承载的是经过换算的参数,每台路由器需要将其换算回再计算新开销 ...
- 如何在html文件中导入header、footer等
1.include是php函数,所以确实需要转化成.php文件--(其实除了用php,html都有自带的引入方法)2.html转化为php文件很简单,直接改一下后缀名就可以了--(如:index.ht ...
- CSS十一问——好奇心+刨根问底=CSSer
最近有时间,想把酝酿的几篇博客都写出来,今天前端小学生带着10个问题,跟大家分享一下学习CSS的一些体会,我觉得想学好CSS,必须保持一颗好奇心和刨根问底的劲头,而不是复制粘贴,得过且过.本人能力有限 ...
- Python——连接数据库
好用的教程(*^▽^*):https://www.cnblogs.com/fatcat132006/p/4081576.html
- Asp.NetCore WebApi 引入Swagger
一.创建一个Asp.NetCore WebApi 项目 二.引入NuGet包 SwashBuckle.AspNetCore 三.在项目属性配置中设置 四.修改项目的启动文件Startup.cs 1). ...
- 在项目中导入import javax.servlet 出错解决办法
我们有时会把别人的项目copy到自己这里进行二次开发或者参考学习,有的时候会发生下图的错误,即eclipse项目里我们导入的项目里提示HttpServletRequest 不能引用,会伴随头疼的小红叉 ...
- javascript获取滚动条位置(兼容所有浏览器)
有两种方式来获取浏览器滚动条的位置 第一种:document.documentElement.scrollTop 第二种:$("body").scrollTop() 第一种方式能够 ...
- NopI 导出数据
protected void exportAward(DataSet dsResult) { if (dsResult != null) { string fileName = System.Web. ...
- HAProxy负载均衡安装配置
1.下载HAProxy http://haproxy.1wt.eu/download/1.4/src/haproxy-1.4.22.tar.gz 2. 安装haproxy #tar z ...