springNotes

spring两大核心机制:

  - IoC(控制反转)/DI(依赖注入)
  - AOP(面向切面编程)

1.IoC

  • maven依赖
              <dependencies>
                  <dependency>
                        <groupId>junit</groupId>
                        <artifactId>junit</artifactId>
                        <version>4.11</version>
                        <scope>test</scope>
                  </dependency>
                  <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-context</artifactId>
                        <version>5.0.11.RELEASE</version>
                  </dependency>
                  <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aop</artifactId>
                        <version>5.0.11.RELEASE</version>
                  </dependency>
                  <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                        <version>5.0.11.RELEASE</version>
                  </dependency>
                  <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjweaver</artifactId>
                        <version>1.9.5</version>
                  </dependency>
                  <dependency>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>1.18.6</version>
                        <scope>provided</scope>
                  </dependency>
          </dependencies>
          <build>
              <resources>
                  <resource>
                      <directory>src/main/java</directory>
                      <includes>
                          <include>**/*.xml</include>
                      </includes>
                  </resource>
                  <resource>
                      <directory>src/main/resources</directory>
                      <includes>
                          <include>**/*.xml</include>
                          <include>**/*.properties</include>
                      </includes>
                  </resource>
              </resources>
          </build>
  • 创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;
    private int age;
}
  • 通过IoC创建对象,配置所需的bean,默认通过无参构造函数来获取bean
    <bean id="student" class="cn.ty.pojo.Student">
        <property name="id" value="1"></property>
        <property name="age" value="18"></property>
        <property name="name" value="张三"></property>
    </bean>
  • 通过id获取所需对象
        ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student)application.getBean("student");
        System.out.println(student);
  • 通过运行时类获取bean
        ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student)application.getBean(Student.class);
        System.out.println(student);
        //这种方式运行时,bean的类只能有一个id即实例对象,否则会报错
  • 通过有参构造函数来获取bean
    <bean id="student1" class="cn.ty.pojo.Student">
        <constructor-arg name="id" value="2"></constructor-arg>
        <constructor-arg name="age" value="19"></constructor-arg>
        <constructor-arg name="name" value="李四"></constructor-arg>
    </bean>
  • 给bean中注入集合
实体类
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
          private int id;
          private String name;
          private int age;
          List<Address> address;
      }  
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class Address {
          private int id;
          private String name;
      }

IoC注入

    <bean id="address" class="cn.ty.pojo.Address">
        <property name="id" value="1"></property>
        <property name="name" value="长安"></property>
    </bean>
    <bean id="address1" class="cn.ty.pojo.Address">
        <property name="id" value="2"></property>
        <property name="name" value="太湖"></property>
    </bean>

    <bean id="user" class="cn.ty.pojo.User">
        <property name="id" value="1"></property>
        <property name="name" value="王五"></property>
        <property name="age" value="19"></property>
        <property name="address">
            <list>
                <ref bean="address"></ref>
                <ref bean="address1"></ref>
            </list>
        </property>
    </bean>
  • scope作用域
    1.singleton:单例,表示通过IoC获取的bean是唯一的,无论业务代码是否获取容器中bean,当加载applicationContext.xml时就会创建bean
    2.prototype:原型,表示通过IoC获取的bean时不唯一的,当业务代码获取bean时,才通过无参构造去创建对应的bean
    3.request:请求,表示在一次HTTP请求中有效
    4.session:回话,表示在一个用户会话内有效

  • spring的继承
    与java不同的是,spring的继承是基于对象的继承,子对象包含父对象的所有属性及其属性值,同时还可以添加其他的属性值

    <bean id="usr" class="cn.ty.pojo.User" parent="user">
        <property name="name" value="小七"></property>
    </bean>
User(id=1, name=小七, age=19, address=[Address(id=1, name=长安), Address(id=2, name=太湖)])
  • spring的依赖
    spring的依赖也是对象与对象之间的关系,配置依赖后,被依赖的bean一定先创建,然后再创建依赖的bean,depends-on
  • p命名空间
    p命名空间是为了更方便的完成bean的配置以及bean之间的依赖注入
    <bean id="student3" class="cn.ty.pojo.Student" p:id="3" p:age="20" p:name="小八"></bean>
  • IoC的自动装载(Autowire)
    1. byName 按照属性名来匹配与属性名相同的bean的id
 <bean id="user1" class="cn.ty.pojo.User" autowire="byName">
        <property name="id" value="2"></property>
        <property name="name" value="God"></property>
        <property name="age" value="10"></property>
</bean>
User(id=2, name=God, age=10, address=[Address(id=1, name=长安)])
  2.byType 按照属性名的类型来匹配与属性类型相同的bean,但是如果存在两个相同类型的bean时会报错
    <bean id="user2" class="cn.ty.pojo.User" autowire="byType">
        <property name="id" value="3"></property>
        <property name="name" value="chief"></property>
        <property name="age" value="20"></property>
    </bean>
  • spring的工厂方法

IoC通过工厂创建的bean有两种方式:
1.静态工厂

实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
    private int id;
    private String name;
}
public class CarFactory {
    private static Map<Integer,Car> carMap;
    static {
       carMap = new HashMap<>();
       carMap.put(1, new Car(1, "辉腾"));
       carMap.put(2, new Car(2, "凯迪拉克"));
    }
    public  static Car getCar(Integer id){
        return carMap.get(id);
    }
}
配置工厂
  <!--静态工厂创建bean-->
    <bean id="car" class="cn.ty.pojo.CarFactory" factory-method="getCar">
        <constructor-arg value="2"></constructor-arg>
    </bean>
测试:
Car(id=2, name=凯迪拉克)

2.实例工厂

实体类
public class InstanceFactory {
    private Map<Integer,Car> carMap;
    public InstanceFactory(){
        carMap = new HashMap<>();
        carMap.put(1, new Car(1, "辉腾"));
        carMap.put(2, new Car(2, "凯迪拉克"));
    }
    public   Car getCar(Integer id){
        return carMap.get(id);
    }
}
配置工厂
    <!--实例工厂创建bean-->
    <bean id="instanceFactory" class="cn.ty.pojo.InstanceFactory"></bean>
    <bean id="car1" class="cn.ty.pojo.Car" factory-bean="instanceFactory" factory-method="getCar">
        <constructor-arg value="1"></constructor-arg>
    </bean>
测试:
Car(id=1, name=辉腾)
  • AOP

    AOP就是将一个类中的某个方法抽象成一个切面对象,动态的将代码切入到切面对象的某个位置

创建个接口
public interface Cal {
    public int sum(int num1,int num2);
    public int sub(int num1,int num2);
}
实现类
public class CalImpl implements Cal {
    @Override
    public int sum(int num1, int num2) {
        System.out.println("sum"+"的参数是"+num1+"和"+num2);
        int result = num1 + num2;
        System.out.println("sum"+"的结果是"+result);
        return result;
    }

    @Override
    public int sub(int num1, int num2) {
        System.out.println("sub"+"的参数是"+num1+"和"+num2);
        int result = num1 - num2;
        System.out.println("sub"+"的结果是"+result);
        return result;
    }
}

上述代码中,日志跟代码的耦合度很高,不利于代码的维护,可用AOP来实现优化
1.动态代理实现

  给业务代码找个代理,专门做打印日志的工作,这样业务代码只需关注自身的业务
public class ProxyCalImpl implements Cal {
    @Override
    public int sum(int num1, int num2) {
        int result = num1 + num2;
        return result;
    }

    @Override
    public int sub(int num1, int num2) {
        int result = num1 - num2;
        return result;
    }

    @Override
    public int mul(int num1, int num2) {
        int result = num1 * num2;
        return result;
    }

    @Override
    public int dev(int num1, int num2) {
        int result = num1 / num2;
        return result;
    }
}
动态代理
public class MyInvocationHandler implements InvocationHandler {

    //接收委托的对象,就是Cal
    public Object object;

    //返回代理对象
    public Object bind(Object object) {
        this.object = object;
        //Proxy.newProxyInstance(类加载器,要实现的代理类的接口列表,InvocationHandler类型的对象即要创建动态代理的对象)
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + "的参数是" + Arrays.toString(args));
        Object result = method.invoke(this.object, args);
        System.out.println(method.getName()+"的结果是"+result);
        return result;
    }

测试

    public static void main(String[] args) {
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        Cal cal = new ProxyCalImpl();
        Cal calImpl = (Cal) myInvocationHandler.bind(cal);  //注意这里返回的是代理对象的接口,否则报错
        calImpl.sum(1, 2);
        calImpl.sub(4, 2);
    }
}
sum的参数是[1, 2]
sum的结果是3
sub的参数是[4, 2]
sub的结果是2

2. AOP实现

  - schema方式实现
        1.创建通知类
            public class AopBefore implements MethodBeforeAdvice {
                  @Override
                  public void before(Method method, Object[] args, Object target) throws Throwable {
                      System.out.println(target.getClass().getName()+"执行的方法是"+method.getName());
                     }
              }
            public class AopAfter implements AfterReturningAdvice {
                  @Override
                  public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
                      System.out.println(target.getClass().getName()+"执行的方法是"+method.getName()+"返回值是"+returnValue.toString());
                  }
              }
        2.配置Aop
               <bean id="before" class="cn.ty.log.AopBefore"></bean>
               <bean id="after" class="cn.ty.log.AopAfter"></bean>
               <bean id="calImpl" class="cn.ty.utils.impl.ProxyCalImpl"></bean>
               <aop:config>
                   <aop:pointcut id="myPoint" expression="execution(* cn.ty.utils.impl.*.*(..))"/>
                   <aop:advisor advice-ref="before" pointcut-ref="myPoint"></aop:advisor>
                   <aop:advisor advice-ref="after" pointcut-ref="myPoint"></aop:advisor>
               </aop:config>
        3.测试
                  public static void main(String[] args) {
                      ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
                      Cal calImpl = (Cal)application.getBean("calImpl");
                      calImpl.sum(1, 2);
                      calImpl.sub(4, 2);
                  }
          结果:
              cn.ty.utils.impl.ProxyCalImpl执行的方法是sum
              3
              cn.ty.utils.impl.ProxyCalImpl执行的方法是sum返回值是3

              cn.ty.utils.impl.ProxyCalImpl执行的方法是sub
              2
              cn.ty.utils.impl.ProxyCalImpl执行的方法是sub返回值是2

 

   - AspectJ方式
        1.通知类:
              public class MyAdvice {
                  //前置通知
                  public void before() {
                      System.out.println("执行前置通知");
                  }

                  //后置通知
                  public void after(JoinPoint joinPoint, Object result) {
                    System.out.println(joinPoint.getSignature().getName()+"后置通知");
                  }
                    
                   //环绕通知
                  public Object round(ProceedingJoinPoint joinpoint) throws Throwable {
                      System.out.println("环绕通知:执行前");
                      Object proceed = joinpoint.proceed();
                      System.out.println("环绕通知:执行后");
                      return proceed;
                        }
              }
        2. 配置Aop
              <bean id="myAdvice" class="cn.ty.log.MyAdvice"></bean>
              <aop:config>
                  <aop:aspect ref="myAdvice">
                      <aop:pointcut id="myPoint" expression="execution(* cn.ty.utils.impl.*.*(..)) "/>
                      <aop:before method="before" pointcut-ref="myPoint" ></aop:before>
                      <aop:after-returning method="after" pointcut-ref="myPoint" returning="result"></aop:after-returning>
                      <aop:around method="round" pointcut-ref="myPoint"></aop:around>
                  </aop:aspect>
             </aop:config>
        3. 测试:
                ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
                Cal calImpl = (Cal)application.getBean("calImpl");
                calImpl.sum(1, 2);
                calImpl.sub(4, 2);
           结果:
              执行前置通知
              环绕通知:执行前
              3
              环绕通知:执行后
              sum后置通知
              执行前置通知
              环绕通知:执行前
              2
              环绕通知:执行后
              sub后置通知


        - 注解配置
              1. 在切面类中添加注解:
                    @Aspect:表示该类是切面类。   
                    @Component:将该类的对象注入到 IoC 容器。
                    @Aspect
              @Component
              public class LoggerAspect {
              //前置通知
              @Before(value = "execution(* cn.ty.utils.impl.*.*(..))")
               public  void before(JoinPoint joinPoint){
                      System.out.println(joinPoint.getSignature().getName()+"执行前置通知");
                  }
              //有返回值的后置通知,returning里的值一定要与,方法参数的一致
              @AfterReturning(value = "execution(* cn.ty.utils.impl.*.*(..))",returning = "result")
               public void after(JoinPoint joinPoint,Object result){
                      System.out.println(joinPoint.getSignature().getName()+"执行后置通知");
                  }
              @Around(value = "execution(* cn.ty.utils.impl.*.*(..))")
               public Object AroundReturning(ProceedingJoinPoint joinPoint){
                    Object[] args = joinPoint.getArgs();
                    String arg= Arrays.toString(args);
                    System.out.println(joinPoint.getSignature().getName()+"执行环绕通知前:参数是"+arg);
                    Object proceed = null;
                    try {
                        proceed = joinPoint.proceed();
                        System.out.println(joinPoint.getSignature().getName()+"执行环绕通知后:返回值为"+proceed);
                    } catch (Throwable throwable) {
                        throwable.printStackTrace();
                    }
                    return proceed;
                  }
              }   
              业务类中也要添加  @Component注解   
        2. 在配置文件中添加配置
              <!-- 扫描包 -->                                                      
              <context:component-scan base-package="cn.ty"></context:component-scan>
              <!-- 让 Spring 框架结合切面类和目标类自动生成动态代理对象 -->
              <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
        3.测试:
                ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
                Cal calImpl = (Cal)application.getBean("calImpl");
                calImpl.sum(1, 2);
                calImpl.sub(4, 2);
         结果:
              sum执行环绕通知前:参数是[1, 2]
              sum执行前置通知
              3
              sum执行环绕通知后:返回值为3
              sum执行后置通知
              sub执行环绕通知前:参数是[4, 2]
              sub执行前置通知
              2
              sub执行环绕通知后:返回值为2
              sub执行后置通知   
  • 事务配置

    声明式事务配置:
            <bean id="txManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
            </bean>
            <!-- 哪些方法需要有事务管理 -->
            <!-- 事务传播机制默认值为:REQUIRED   -->   
            <tx:advice id="tx" transaction-manager="txManger">
              <tx:attributes>
            <tx:method name="ins*" propagation="NESTED" isolation="DEFAULT"/>
            <tx:method name="del*" rollback-for="java.lang.Execption"/>
            <tx:method name="*" read-only="true" no-rollback-for=""/>
              </tx:attributes>
            </tx:advice>
                 <!-- 声明式事务 切点范围设置大点 -->
        <aop:config>
            <aop:pointcut expression="execution(* cn.ty.Service.impl.*.*(..))" id="mypoint"/>
            <aop:advisor advice-ref="tx" pointcut-ref="mypoint"/>
        </aop:config>                                       
    
posted @ 2020-05-09 00:30  shallMe  阅读(245)  评论(0)    收藏  举报