1.IOC全注解(annocation)
1.1 在配置文件中告诉spring去哪里扫描实体对象
如果要使用ioc注解,一定要配置一个<context:component-scan base-package=""> ,让spring去扫描包及其子包里面的类,如果配置多个包包之间用逗号隔开
<context:component-scan base-package="com.jrsoft.modules,com.jrsoft.engine">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
1.2在类型上面添加anntation
- @Component:一般写在工具类上面
- @Service:一般写在服务对象上
- @Repository:一般写在Dao上面
- @Controller:一般写在Action上,并且在springMVC中,Controller就在标示一个Controller对象 这四个标签对于spring来说作用相同,主要是给程序员看的
1.3具体使用
xml配置中完整bean配置如下
<bean id="" class="" factory-method="" init-method="" destroy-method="" scope=""/>
在对应注解中的使用
- id:所有四个标签中都支持value值,这个值就是id的名字
- class:不用写了
- factory-method和factory-bean没有对应的标签,如果有这种需要,直接配置到xml中(annocation和xml可以混合使用)
- init-method:使用@PostConstruct直接放在init方法上。(注意initMethod一定是在DI之后执行的)
- destory-method:使用@PreDestroy直接放在destroy方法上
- scope:在类上面添加@Scope("prototype") 示例
@Component
@Scope("prototype")
public class JdbcUtil {
@PostConstruct
public void init(){
System.out.println("初始化DataSource");
}
@PreDestroy
public void close(){
System.out.println("DataSource关闭");
}
}
2.静态代理
2.1问题分析
传统session操作中事务的开启和关闭在dao层,当一条业务需要多个dao来完成是,这样会造成无法控制事务问题 解决办法
- sessionFactory.getCurrentSession得到当前线程中的session
- 把session的开启、事务的开启和事务的提交从dao中移到service方法中 (我们应用中,事务都应该开在service的方法上面)
事务直接开在service中存在的问题
存在一个事务跨层问题,如果直接把事务管理的代码写在service上会造成service代码污染,最好的方式就是将事务的代码从service中抽出来,使用代理模式再加到目标代码中
2.2代码实现
目标对象
public class EmployeeServiceImpl implements EmployeeService{
@Override
public void save() {
System.out.println("dao.save()");
}
@Override
public void update() {
System.out.println("dao.update()");
}
}
代理对象
public class EmployeeTranscationServiceImpl implements EmployeeService{
private EmployeeService target;
public EmployeeTranscationServiceImpl(EmployeeService target) {
this.target = target;
}
@Override
public void save() {
System.out.println("session.getCurrentSession");
System.out.println("session.getTranscation().begin()");
target.save();
System.out.println("session.getTranscation().commit()");
}
@Override
public void update() {
System.out.println("session.getCurrentSession");
System.out.println("session.getTranscation().begin()");
target.save();
System.out.println("session.getTranscation().commit()");
}
}
配置
<bean id="target" class="com.jrsoft.gdb.test.EmployeeServiceImpl" />
<bean id="EmployeeService" class="com.jrsoft.gdb.test.EmployeeTranscationServiceImpl">
<constructor-arg ref="target" />
</bean>
测试
@RunWith(SpringJUnit4ClassRunner.class)
public class StaticProxyTest {
@Autowired
@Qualifier("employeeService")
private EmployeeService service;
@Test
public void testSave(){
service.save();
}
}
静态代理分析
静态代理确实出来了代码污染问题,但仍然存在以下两个问题:
- 重复的代码仍然分散在各个方法中
- 需要为每一个真实对象写一个代理对象
3.动态代理
例子:拦截器
3.1 有接口对象的动态代理
- 客户端调用save方法,执行被InvocationHanalder拦截
- 在invoke方法中执行添加的额外的逻辑
- 调用m.invoke(target,args)执行到了真实的目标对象上对应的方法
- 真实对象返回执行结果到InvocationHanalder里面
- 执行完其他逻辑,把结果返回给客户端 相同的ClassLoader加载的类才能相互访问
JDK中提供了针对有接口对象的动态代理实现方式
public class TranscationInvocationHandler implements InvocationHandler {
private IEmployeeService target;
private TranscationManager txManager;
public TranscationInvocationHandler(IEmployeeService target, TranscationManager txManager) {
this.target = target;
this.txManager = txManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
txManager.begin();
try{
Object ret = method.invoke(target,args);
txManager.commit();
return ret;
}catch (Exception e){
txManager.rollback();
}
return null;
}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class DynProxyTest {
@Autowired
private IEmployeeService service;
@Autowired
private TranscationManager txManager;
@Test
public void testSave(){
IEmployeeService o = (IEmployeeService)Proxy.newProxyInstance(service.getClass().getClassLoader(),
new Class[] {IEmployeeService.class},new TranscationInvocationHandler(service,txManager));
o.update();
}
}
3.2 没有接口的对象的动态代理(使用继承)
public class LogInvocationHandler implements InvocationHandler {
private Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("您当前调用了"+method);
return method.invoke(target,args);
}
}
测试
public class GclibTest {
@Test
public void testSave(){
SomeBean target = new SomeBean();
//增强器
Enhancer enhancer = new Enhancer();
//设置目标对象的classLoader
enhancer.setClassLoader(target.getClass().getClassLoader());
//设置这个动态代理类的父类
enhancer.setSuperclass(SomeBean.class);
//设置要传入的拦截器
enhancer.setCallback(new LogInvocationHandler(target));
//使用cleate方法创建代理对象
SomeBean o = (SomeBean)enhancer.create();
o.prinnt1();
}
}
4.Spring AOP
AOP是一种更高级的动态代理的使用,中文解释为:面向切面编程(Aspect Oritention Programing)
4.1 AOP中的重要概念
- 切入点:要加入业务逻辑的点(在哪些类的哪些方法上)
- 通知:通知包含两个方面,1:代表方法的执行时间,2:在这个时间上面要做什么事情
- 切面:一个切入点+一个通知(在什么地方,什么时候,做什么事情)
- 织入:把一个切面应用到真实对象上面的过程就叫织入 在java中,没有语言能够准确的描述切入点,所以有一个AspectJ,这是一种提供了描述切入点的语言
4.2 Spring中AOP的使用方式
1.使用xml配置
<aop:config>
<!--配置一个切入点,expression:切入点的表达式,id:切入点的名称-->
<aop:pointcut id="po" expression="execution(* com.jrsoft..*Impl.*(..)) || execution(* com.jrsoft.business..*Impl.*(..))"/>
<!--配置一个切面,代表在这个切面的定义中,所有的做什么事情,都是有txManager这个对象的方法提供的-->
<aop:aspect ref="txManager">
<!--before代表在方法执行之前-->
<aop:before method="begin" pointcut-ref="po"/>
<!--after-returning代表方法正常执行之后-->
<aop:after-returning method="commit" pointcut-ref="po"/>
<!--after-throwing代表在方法抛出异常的时候-->
<aop:after-throwing method="begin" pointcut-ref="po"/>
</aop:aspect>
</aop:config>
切入点表达式
没有实现接口对象的配置
<bean id="myservice" class="org..MyService"/>
<aop:config>
<!--配置一个切入点,expression:切入点的表达式,id:切入点的名称-->
<aop:pointcut id="po" expression="execution(* com.jrsoft..*Impl.*(..)) || execution(* com.jrsoft.business..*Impl.*(..))"/>
<!--配置一个切面,代表在这个切面的定义中,所有的做什么事情,都是有txManager这个对象的方法提供的-->
<aop:aspect ref="myservice">
<!--before代表在方法执行之前-->
<aop:before method="begin" pointcut-ref="po"/>
<!--after-returning代表方法正常执行之后-->
<aop:after-returning method="commit" pointcut-ref="po"/>
<!--after-throwing代表在方法抛出异常的时候-->
<aop:after-throwing method="begin" pointcut-ref="po" throwing="ex"/>
</aop:aspect>
</aop:config>
public class MyService{
/**
* 在方法正常执行之后
*/
public void afterReturning() {
}
/**
* 在方法执行之后,在finally里面
*/
public void update() {
}
/**
* 在抛出异常的时候,如果要得到抛出的异常,1:在方法中添加一个参数,起一个参数名字,2:在<aop:after-throwing中添加一个throwing属性,
* 属性的名字就是这个方法的参数的名字
*/
public void afterThrowing() {
}
public Object around(ProceedingJoinPoint pjp) {
try {
System.out.println("before");
Object ret = pjp.proceed();
System.out.println("after returning");
}catch (Throwable ex){
System.out.println("after throwing");
}finally {
System.out.println(after);
}
return null;
}
}
小结
- 准备一个真实的对象,准备一个类,这个类里面的方法用来提供通知里面做什么
- 这两个类都要配置到spring容器中
- 配置springAOP
引入AOP命名空间
<aop:config>
<aop:pointcut id="" expression=""/>
<aop:aspect ref="">
<aop:before method="" pointcut-ref=""/>
<aop:after-returning method="" pointcut-ref=""/>
<aop:after-throwing method="" pointcut-ref=""/>
</aop:aspect>
</aop:config>
- 我们在客户端还是注入的是spring里面配置的真实对象,实际上spring在为这些对象创建好代理对象之后,会使用这些创建好的代理对象去替换掉容器中的原始对象
SpringAOP执行流程
- 解析xml
- 实例化所有bean
- 解析aop:config
Spring中使用标签完成AOP
要使用annotation的AOP,需要在xml中引入aspectj-autoproxy,这个元素相当于引入了用于解析@Aspect和@Before等元素的第三方程序。
<aop:aspectj-autoproxy/>
应用示例
/**
* @Aspect标签相当于 <aop:aspect ref=""/>
*/
@Aspect
@Component
public class EmployeeServiceImpl{
/**声明一个pointcut
* <aop:pointcut id="pc" expression="execution(* com.jrsoft..*Impl.*(..))"/>
*/
@Pointcut("execution(* com.jrsoft..*Impl.*(..))")
public void pc(){
}
/**
* <aop:before method="before"/>
* 1.可以直接写point-cut表达式
* 2.使用定义好的pointout
*/
@Before("pc()")
public void before(){
}
/**
* 在方法正常执行之后
*/
@AfterReturning("pc()")
public void afterReturning(){
}
@After("pc()")
public void after(){
}
/**
* 在抛出异常的时候,如果要得到抛出的异常,1:在方法中添加一个参数,起一个参数名字,2:在<aop:after-throwing中
* 添加一个throwing属性,属性的名字就是这个方法的参数名字
* @param ex
*/
@AfterThrowing(pointcut = "pc()",throwing = "ex")
public void afterThrowing(Throwable ex){
}
}
spring中AOP的通知类型:
- aop:before 前置通知
- aop:after-returning 后置通知
- aop:after 最终通知
- aop:after-throwing 异常通知
- aop:around 环绕通知