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



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

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>

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执行后置通知

  • 事务配置

    声明式事务配置:





    <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  阅读(253)  评论(0)    收藏  举报