Spring-day02

Annotation复习:
1,Annotation:作为类型的元数据;
  1,给类型加标记;
  2,annotation可以添加各种类型的属性;
2,Annotation的上的标记:
  1),target:标签可以加在什么类型上的;
    1),TYPE:所有类和接口;
    2),METHOD:方法;
    3),FIELD:字段上面;
    4),PARAMETER:方法的参数上面;
    5),CONSTRUCTOR:构造器方法;
  2),retention:把标签保留到什么时候;
    1),CLASS:保留到字节码;
    2),SOURCE:保留到代码;
    3),RUNTIME:保留到运行时;

3,标签一般需要三方参与:
  1),标签类型;
  2),打标签的目标类型;
  3),解析程序;
  4),注解的参数:
    1),注解中属性的添加方式;
    2),注解中属性的默认值;
    3),getAnnotation方法的使用;

Spring的注解使用;

DI注解的使用:
1,使用DI注解,在spring的配置文件里面还是需要配置bean本身;
2,@Autowired
  1),autowired标签可以把需要的对象自动的注入到目标对象中;
  2),autowired标签可以放在字段上,也可以放在setter方法上面;
  3),autowired的执行流程:
    1),加载和解析XML文件;
    2),实例化所有的bean,并且方到spring的容器当中;
    3),解析对象的类型,如果对象类型的某些字段或者setter方法上面有@Autowired标签,
      1),在容器中找到对应的对象;
      2),把找到的对象设置到字段或者setter方法中;
  4),怎么找对象?
    1),首先按照类型找;
    2),如果没有找到;系统报错(默认情况下,autowired标签有一个@Required标签的作用)
    3),如果找到多个,按照字段或者属性的名字,再用名字+类型去找;
    4),如果1,3都没有找到,报错;
    5),可以通过设置autowired的required=false让这个属性(字段)可以没有值;
    6),可以通过在字段或者setter方法上添加@Qualifier来规定我要注入的bean的名字;
    7),可以在Spring的主要对象上面添加@Autowired,让spring自动注入;
    8),所以,我们的spring的测试类,可以直接引用需要测试的目标对象就可以了;

3,@Resource
  1),也可以通过resource标签来自动注入;
  2),怎么找对象?
    1),首先按照字段或者属性的名字找;
    2),如果找不到,报错(resource关联的对象必须存在)
    3),如果按照名字找不到,再按照类型来找;
    4),可以通过resource的name属性来指定一个bean注入;如果一旦设置了resource的name属性,就只能按照名字找了;
    5),因为resource是javax的标签,所以resource不能注入spring中关键的对象;

选择:
1,Resource是JavaEE的规范,但是缺少spring对autowired标签的一些加强;
2,一般我们选择使用@Autowired标签;

使用IoC注解:

1,在配置文件中告诉spring去哪里扫描实例对象;

    <!-- 
        如果要使用IOC的注解,一定要配置一个context:component-scan,
        base-package表示,让Spring去扫描包及其子包下的类(是否带有IOC注解)
        如果要配置多个包,包之间用逗号隔开
     -->
    <context:component-scan base-package="com.rk1632._03_annotation_ioc"/>

2,在类型上面添加anntation:
  1),@Component:一般我们写在工具类上面;
  2),@Service:一般我们写在服务对象上;
  3),@Repository:一般写在DAO上面;
  4),@Controller:一般写在Action上面;并且在SpringMVC中,Controller就在标示一个Controller对象;
3,这四个标签对于spring来说,作用相同,主要是给程序员看的;

<bean id="" class="" factory-method="" factory-bean="" init-method="" destory-method="" scope=""/>
1,id:所有的四个标签都支持value值,这个值就是id的名字;
2,class:不用写了;
3,factory-method和factory-bean就没有对应的标签了;如果有这种需要,直接配置到XML里面(XML和Annocation是可以混用的)
4,init-method:使用@PostConstruct直接放在init方法上面;注意:initmethod一定是在DI(依赖注入)之后执行的;
5,destory-method:使用@PreDestroy直接放在destory方法上面;
6,scope,在类上面添加@Scope("prototype");

代理模式

提出问题:
问题:事务是开在dao上面的,所以如果在service中调用多个dao的方法,如果在这些方法执行过程中报错,事务没法回滚

对于我们的应用,事务都应该开在service的方法上面

解决办法:
1,sessionFactory.getCurrentSession:得到当前线程中的session;
2,把session的开启和事务的开启,事务的提交从dao中移到service方法中

存在一个事务跨层的问题,但是如果直接把事务管理的代码写在service中,会造成service代码污染,最好的方式就是把事务的代码从service中抽取出来,使用代理模式,再加到目标代码中:

静态代理:

public class EmployeeServiceImpl implements IEmployeeService {

    public void save(Employee e) {
        
        System.out.println("save Employee");
    }

    public void update(Employee e) {
        
        System.out.println("update Employee");
    }

}
public class EmployeeTranscationServiceImpl implements IEmployeeService {

    private IEmployeeService target;
    
    public EmployeeTranscationServiceImpl(IEmployeeService service){
        this.target = service;
    }
    
    @Override
    public void save(Employee e) {
        System.out.println("sessionFactory.getCurrentSession");
        System.out.println("session.getTranscation().begin()");
        target.save(e);
        System.out.println("session.getTranscation().commit()");
    }

    @Override
    public void update(Employee e) {
        System.out.println("sessionFactory.getCurrentSession");
        System.out.println("session.getTranscation().begin()");
        target.update(e);
        System.out.println("session.getTranscation().commit()");
    }

}

 

静态代理分析:

静态代理分析:
1,静态代理确实处理了代码污染的问题;

问题:
1,重复的代码仍然分散在了各个方法中;
2,需要为每一个真实对象写一个代理对象;

Struts中的拦截器:

动态代理

1,有接口的对象的动态代理
  1),JDK中提供了针对有接口对象的动态代理实现方式;
2,没有接口的对象的动态代理
  1),使用继承的方式完成动态代理;javassist/cglib

1,客户端调用save方法,执行被InvocationHanalder拦截;
2,在invoke方法中执行添加的额外的逻辑;
3,调用m.invoke(target,args)执行到了真实的目标对象上的对应方法;
4,真实对象返回执行结果到InvocationHandler里面;
5,执行完成其他逻辑,把结果返回给客户端;

使用JDK的动态代理流程:
1,使用JDK的动态代理,必须要保证代理的真实对象是有接口的;

    /**
     * proxy:代理出来的对象
     * method:这次调用的方法
     * args:这次调用方法传入的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        }

2,需要实现一个InvocationHandler,并实现invoke方法(如上);
  0),需要给InvocationHanlder的实现类提供真实对象;
  1),在invoke方法中执行自己的额外添加的业务逻辑;
  2),在编写完额外逻辑之后,调用method.invoke(target,args)执行真实对象上的对应方法;
  3),得到真实方法执行的结果;
  4),处理结果,并把结果返回给客户端;

        //Proxy:专门用来生产代理类的对象
        //newProxyInstance:需要三个参数
        //1,ClassLoader():专门用来加载类的
        //2,interfaces:需要接口的数组,这个接口就是我们目标对象实现的接口
        //3,处理动态代理的类的对象
       IEmployeeService service = (IEmployeeService)Proxy.newProxyInstance(service.getClass().getClassLoader(), 
                        new Class[]{IEmployeeService.class}, 
                        new TranscationInvocationHandler(service,txManager));

3,在客户端调用Proxy.newProxyInstance方法(如上):
  1),需要传入目标对象的classloader;
  2),需要传入目标对象的接口类型;
  3),需要传入一个自定义的InvocationHandler的实例;

使用CGLIB完成没有接口的对象的动态代理

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("操作日志:操作时间:"+new Date()+", 当前调用的方法:"+method.getName()+", 传入的参数:"+Arrays.toString(args));
        return method.invoke(target, args);
    }

}
    @Test
    public void test() throws Exception {
        SomeBean bean = new SomeBean();
        
        //增强器
        Enhancer enhancer = new Enhancer();
        //设置目标对象的ClassLoader
        enhancer.setClassLoader(bean.getClass().getClassLoader());
        //设置这个动态代理的父类
        enhancer.setSuperclass(SomeBean.class);
        //设置要传入的拦截器
        enhancer.setCallback(new LogInvocationHandler(bean));
        //使用create方法创建代理对象
        SomeBean o = (SomeBean) enhancer.create();
        o.print1();
    }

小结:
1,动态代理处理:重复的代码仍然分散在了各个方法中这个问题;
2,动态代理需要调用代码为每一个类去创建动态代理对象;
3,动态代理最小的单位就是一个类;
4,如果只想处理一个类型中的某些方法,在invoke方法中根据当前执行的方法名称去做判断就可以了;

Spring的AOP:

AOP就是一种更高级的动态代理的使用;
Aspect Oritention Programming(面向切面编程)

切入点:要加入业务逻辑的点(在哪些类的哪些方法上面;)
通知:通知包含两个方面,1,代表方法的执行时间,2,在这个时间上面要做什么事情;
切面:一个切入点+一个通知=一个切面(在什么地方,在什么时候,做什么事情);
织入:把一个切面应用到真实对象上面的过程,就叫做织入;

在Java中,没有语言能够准确的描述切入点;所以,有一个AspectJ,这是一种语言,提供了用于描述切入点的语言;

使用Spring的AOP:
1,使用XML的配置方式;
2,使用Annotation的配置方式;

定义切入点的表达式

如果可以是任意的值,使用*就可以了;

定义切入点表达式的例子如下:

aop包中,所有的service的所有的方法;
execution(* com._520it.spring.day2.aop.*Service.*(..));

day2中所有service的save方法;
execution(* com._520it.spring.day2..*Service.save(..))

    <!-- 配置AOP -->
    <aop:config>
        <!-- 配置一个切入点
            expression:这个切入点的表达式
            id:这个切入点的名称
         -->
        <aop:pointcut expression="execution(* com.rk1632._08_aop.*Service.*(..))" id="pc"/>
        <!-- 配置一个切面 
            ref:代表在这个切面的定义中,所有的做什么事情,都是由txManager这个对象的方法提供的
        -->
        <aop:aspect ref="txManager">
            <!-- before代表方法执行之前 -->
            <aop:before method="begin" pointcut-ref="pc"/>
            <!-- after-returning代表方法正常执行之后 -->
            <aop:after-returning method="commit" pointcut-ref="pc"/>
            <!-- after-throwing代表方法抛出异常的时候 -->
            <aop:after-throwing method="rollback" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

Spring中AOP的通知类型:
aop:before:前置通知;
aop:after-returning:后置通知;
aop:after:最终通知;
aop:after-throwing:异常通知;
aop:around:环绕通知;

小结:
1,准备了真实的对象,准备一个类,这个类里面的方法用来提供通知里面的做什么;

2,这两个类都要配置到Spring容器中;

3,配置springAOP
  1,引入aop命名空间;
  <aop:config>
    <aop:point-cut expression="" id="" />
    <aop:aspect ref="">
      <aop:before method="" pointcut-ref="">
      <aop:after-returning method="" pointcut-ref="">
      <aop:after-throwing method="" pointcut-ref="">
    </aop:aspect>
  </aop:config>

4,我们在客户端还是直接注入的是在spring里面配置的真实对象;
实际上,spring在为这些对象创建好代理对象之后,会使用这些创建好的代理对象去替换容器中的原始对象;

SpringAOP的执行流程:
1,解析xml;
2,实例化所有的bean;
3,解析aop:config;
  1),解析aop:aspect,得到aspect引用的对象;txManager
  2),解析aop:aspect里面的每一个切面;
    1),得到该aspect对应的pointcut-ref;
    2),得到pointcut-ref对应的pointcut的表达式;
    3),使用表达式中用于匹配类型的表达式;
    4),使用该表达式去和spring里面配置的所有的bean的类型进行匹配;
      1),如果匹配不上,不管;
      2),如果匹配上了,该对象作为spring动态代理的目标对象;
        1),如果该对象实现了接口,使用JDK的动态代理包装;
        2),如果该对象没有实现接口,使用cglib包装;
        3),得到配置的拦截时机+逻辑提供类(txManager)的对应方法(从method解析)+pointcut表达式中方法的匹配模式创建一个拦截器
        4),在把该拦截器使用对应的动态代理机制代理成代理对象;
        5),替换spring容器中的对应bean的实例;

原文地址:https://www.cnblogs.com/Java0120/p/9954652.html