青梅煮酒Spring应用与知识点

Spring应用与知识点

Spring组成

bean

BeanFactory管理bean

我们自己实现一个静态beanfactory,读取bean文件,实例化bean

public class BeanFactory {  
private static Properties props;
private static Map<String, Object> beans;
​
static {//静态,跟随类加载初始化
    try {
        props = new Properties();
        InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");//硬编码
​
•        props.load(in);
​
•        beans = new HashMap<>();//初始化
•    } catch (Exception e) {
•        throw new ExceptionInInitializerError("Cannot find bean.properties");
•    }
}
​
public static Object getBean(String beanName) {
    Object bean = null;
    try {
        String beanPath = props.getProperty(beanName);//得到bean类路径
        bean = Class.forName(beanPath).getDeclaredConstructor().newInstance();//使用反射 实例化
    } catch (Exception e) {
        e.printStackTrace();
    }
​
•    return bean;
}
​
public static Object getSingletonBean(String beanName) {//单例,懒加载
    Object bean = null;
    if(beanName != null) {//输入不为空
        if(beans.containsKey(beanName)) {//已存在,实例化过(有缓存),
            bean = beans.get(beanName);
        } else {
            bean = getBean(beanName);//直接获取
            beans.put(beanName, bean);//没有,(缓存)
        }
    }
​
•    return bean;
}
​
public static IEarPhone getAirPod() {//供xml中方法三使用
    return new AirPod();
}
    

properties文件:

image-20220426000038541

等号左边beanname , 右边是properties,也叫beanPath

使用

image-20220426001938269

上图:this.airpod=xx.getbean("xxx"),这样就不用依赖new实例了由Spring容器实例化。

背景介绍:上图Phone类有个实例字段是耳机类的引用,要解决new AirPod的高耦合问题。

第四种方法无需再 使用new airpod:

image-20220426001707042

正式使用Spring

教材内容:Spring中无论使用那种容器都需要从配置文件中读取JavaBean信息,然后根据信息创建JavaBean实例对象并注入其依赖的属性。要在Spring Ioc容器中获取一个bean,首先要在配置文件中配置bean标签,Spring控制反转机制会根据配置,实例化这个bean实例。

BeanFactory采用了工厂模式,通过从xml配置文件或属性文件中读取JavaBean的定义来创建、配置和管理JavaBean。BeanFactory有很多实现类,其中XmlBeanFactory可以通过流行的XML文件读取配置来加载JavaBean。

image-20220829214640581

使用前获取jar包。

BeanFactory实现了IoC(控制反转)控制。所以可以成为IOC容器, ApplicationContext扩展了BeanFactory容器并添加了国际化和生命周期时间的发布监听等更强大的功能。 6.1 ApplicationContext的特点:1. 默认单例 2. 及时加载,是在load xml文件的时候进行的实例化,bean.xml 文件中读一个就初始化一个

6.2 ApplicationContext接口的三个实现类 ​ ClassPathXmlApplicationContext: 用于读取resources下的XML文件 ​ FileSystemXmlApplicationContext: 可以读取任意位置的XML文件,需要完整的path(绝对路径) ​ AnnotationConfigApplicationContext: 用于读取Annotation ​ 6.3 ApplicationContext:Spring 容器, 容器创造Bean的三种方法 ​ 直接创造Bean; 通过工厂类创造; 通过静态工厂 ​ 6.4 Bean的作用范围 ​ scope:

singleton 单例 ​ prototype 多例 ​ request 在web的环境下,在请求范围内是单例,跨请求多例 ​ session 在web的环境下,在session的范围内是单例,跨session是多例 ​ global-session 在集群环境下,在global session的范围内是单例,跨global session是多例 ​ 在非集群环境下,它等同于session ​ 6.5 Bean生命周期 ​ Singleton模式中: ​ Bean初始化:及时加载,在load XML的时候进行实例化 ​ Bean的存在周期:只要容器存在,Bean就一直存在 ​ Bean的销毁:容器销毁的时候,Bean销毁 ​ Prototype模式中: ​ Bean初始化:懒加载,当要调用getBean的时候进行实例化 ​ Bean的存在周期:由用户代码决定 ​ Bean的销毁:通过JVM的GC来释放,当没有变量指向Bean以后,JVM会在适当的时候GC Bean

image-20220426002355083

下图是xml中方法二的工厂,但是仍需要new airpod,所以一般不用

image-20220426002732333

 ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
​
•    IEarPhone earPhone5 = (IEarPhone) ac.getBean("airPod");
•    System.out.println(earPhone5);
​
•    IEarPhone earPhone6 = (IEarPhone) ac.getBean("airPod");
•    System.out.println(earPhone6);//相同,单例。读文件时加载(实例化)完了,后get时仅仅是返回。多例,不同
​
•    ac.close();//容器关闭时,会调用destory

引入:除引入外,还有library

  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.13.RELEASE</version>
   </dependency>

补充毕设项目:

public class SpringBeanFactory {
    public static Object getBean(String[] paths, String name){
        XmlWebApplicationContext ctx = new XmlWebApplicationContext();
        ctx.setConfigLocations(paths);
        ctx.setServletContext(new MockServletContext(""));
        ctx.refresh();
        return ctx.getBean(name);
    }
    public static Object getBean(String name){
        String[] paths = { "applicationContext.xml" };//注解文件
        return getBean(paths,name);
    }
}

做测试用:

public class CRUDTest extends TestCase {
    
    Logger log = Logger.getLogger(CRUDTest.class);
    
    /**
     * 测试创建
     */
    public void testCreate() {
        IAuthUserService authUserService = (IAuthUserService) SpringBeanFactory.getBean("authUserServiceImpl");
        log.info("------------------ create  start ");
        AuthUser authUser = new AuthUser();
        authUser.setUsername("test0001");
        authUser.setPassword("test0001");
        authUser.setRealname("用户0001");
        authUserService.createSelectivity(authUser);
        System.out.println("id = " + authUser.getId());
        log.info("------------------ create  end ");
    }
    

依赖注入:Dependency Injection

一个类要使用到另一个类的实例时,传统程序通常由调用者来创建被调用者的实例。但在Spring里创建被调用者的工作不再由调用者完成,因此成为控制反转。创建被调用者实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。

Spring在解耦合以后,通过设置完成复杂类实例化。

上例phone的成员变量是耳机类,只需要再bean.xml中装配这个耳机类即可。

而本例A类是一个复杂类,要实例化A这个复杂类(有不同类型的实例字段,需要赋值)

第一种方式构造方法注入

name(get属性去掉get并且首字母变小写后的实例字段)/index(序号,从0开始)/type(类型,按顺序) + value/ref ​ 可以不用ref属性,嵌入bean标签; 可以不用value属性,用value标签

image-20220426230938750

​
           <bean id="a" class="cn.liwang.A">
           <constructor-arg index="1" value="5"></constructor-arg>
​
            <constructor-arg name="i">
                <value>5</value>
            </constructor-arg>
​
         <constructor-arg name="str" value="abc"></constructor-arg>
​
         <constructor-arg name="b" ref="b"></constructor-arg>   <!-- ref内容为已有的beanname-->
​
         <constructor-arg name="b">
             <bean class="cn.liwang.B"></bean>
         </constructor-arg>
​
         <constructor-arg name="c" ref="c"></constructor-arg>
​
         <constructor-arg type="java.lang.Integer" value="6"></constructor-arg>
       </bean>

set方法 注入

教材:简单直观,最常用,设置注入指通过setter方法传入被调用者的实例。value标签为name属性赋值,Spring会把这个标签提供的属性值注入到指定的JavaBean中。

基础类型 + Bean + 集合类型(数组+List+Set\Map+Property)<基础类型,Bean> name(实例字段) + value/ref 可以不用ref,嵌入bean标签; 可以不用value,嵌入value标签 构造方法注入和set方法注入的区别:构造的必须和构造方法的参数表一致,set可以不一致 数组,List,Set : array,list,set这三种标签可混用

image-20220426233640446

image-20220426233724033

注意上图47行不,A类中有ib,为了注释练习,有改动

下图为A类,只看成员变量,先忽略注解

image-20220426234521960

A的使用:

// ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

// A a = (A) ac.getBean("a");这一句是精髓,这样获得而不是直接new A.成员变量赋值也在bean中注入,而不是a.setxxx =xxx

而为什么这么用?

类有改动只需要修改配置文件,用new的方式还需要修改无限的java代码。因为xml文件一个bean可以被复用。


Annotation注解

Annotation 实现IoC/DI 只能用与基础数据类型和bean类型,集合的注入只能用配置文件

 // XML中有:  <bean id="" class="" scope="" init-method="" destory-method="">
        //       <property name="" value=""|ref=""></property>
        //   </bean>  用来与annotation作对比
1.使用注解+xml

8.1 创建对象(实例) ​ xml怎么写: bean(id, class) 与下面作对比 ​ annotation怎么写:

        @Component(value="<id>") 如果id是类名的小驼峰写法,那么可以不用写value
        如果id不是类名的小驼峰模式,需要写,而且ac.getBean那里要一致
        以下三个和Component没有任何区别
        @Controller 一般用于表示层/Web层
        @Service 一般用于业务层/中间件层/service层
        @Repository 一般用于持久层/数据层
8.2 依赖注入注解

xml中:property(name, value) 基础数据

annotation: 1@Value(value="<值>") ​ 2通过@PropertySource({"classpath:<properties文件的路径>"})+@Value(value="${key}") ​ 3还可以:SpEL, Spring 定义的表达式语言 (有点像JS) #{SpEL表达式} ​ xml中:property(name, ref) bean ​ annotation: 1@Autowired,如果刚好有且只有一个类型相符的bean,那么这个bean就可以被注入; 如果一个都找不到,报错, 怎样才能找到?需要 在其同类型的类代码中添加@component注释。见下图B类

image-20220427171035805

image-20220427171121003

2.@Autowired, @Qualifier("<id>") 如果有两个或者两个以上类型相符的bean,那么要看bean的id和当前属性是否有匹配的。@Qualifier必须配合@Autowired使用如果有且仅有一个匹配的,那么成功,如果没有或者有多个,报错 Qualifier不能单独使用

3.@Autowired+@Qualifier("<id>") 等价于 @Resource(name="<id>") 要引入dependency包,import module1.3.2 pom文件addlibrary; 

8.3 作用范围 xml:bean(scope), singleton, prototype ​ annotation: @Scope("singleton|prototype")

在注入时的bean里也可以用,见下图B2类

8.4 生命周期 xml: bean(init-method, destory-method) ​ annotation: @PostConstruct, @PreDestory

分别是bean初始化后与销毁前会被调用的方法。

image-20220427180543930

image-20220427134333737

image-20220427170539603

注意A.java使用了@propertysource,注册了properties文件。

这是B类,

image-20220427134304041

这是B2与B类型相同,不会自动匹配,需要区分

image-20220427134633968

bean类里也可以加作用范围标签

image-20220427134852349

context:component-scanscan scan scan component scan comtext domponentscan 

使用: ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml");

2配置类

在《Spring实战》中介绍注解直接介绍此方式,并直言,配置是一种过时方式,很难想象在没有SpringBoot的情况下开发Spring应用。

8.5 配置类,spring中的新注解 完全替换*.xml annotation: @Configuration 替换xml, @ComponentScan 告知spring在创建容器的时候要扫描包 8.6 问题:

(1)如果需要一个类产生多个实例,比如A(加入想实例化多个A的对象,xml中能罗列多个bean标签,只要ID名不同,class相同,但是注解只能有一个component(实例)); ​ (2)需要实例化的类不是自己写的,jar包中的类无法改动,不能添加注解 ​ 解决:annotation: @Bean 实例化,并且把实例放入Spring容器

@Bean 表明该方法返回的对象,会以bean形式添加到

image-20220427230345297

上图的set方法会被@value注解覆盖,原因看下面工作模式总结,方法赋值在前,set注入在后

为什么这里又用 new字段呢?因为configuration相当于xml ,n这里的@bean相当于xml中的bean标签,都到这一步了耦合是没有问题的,就是要解耦合后在xml注入(与业务类解耦,在配置类中注入,是讲得通的。)

注意在这里@Bean(name = "a2"),属于下面总结的工作模式3,A类里的注释@value等注释对a2仍有效!

8.7工作模式:1 xml + 类中有@Component等 ​ 2 @Configuration + 类中有@Component等 ​ 3 @Configuration, @Bean + 类中有@Component等 ​ 如果: @Component + @Value: 过程是首先根据类的缺省构造方法生成A,然后使用set(@Value)注入 ​ 如果: @Bean + @Value(如a2): 过程是调用@Bean修饰的方法获得A,然后使用set注入。 ​ 可以在@Bean修饰的方法中注入值,但是会被@Value覆盖 ​ 8.8 @Configuration什么时候不可以不写? ​ 8.8.1, 如果配置类被当做参数传给AnnotationConfigApplicationContext构造方法,那么@Configuration可以没有 ​ 8.8.2, 如果配置类没有传给AnnotationConfigApplicationContext构造方法: 1. 必须有@Configuration 2. @Import,在已经引入的配置类中使用

image-20220427235317408

上图这样SpringConfiguration2中的@configuration可以注释掉

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class, SpringConfiguration2.class);

这样参数传进去了也可以注释掉

8.9 @Qualifier在参数中的应用,在配置类的参数中应用,当同参数类型有多个可使用实例时指明是使用Spring容器中的哪个Bean实例

与上面注解成员变量时意义差不多,都是有多个相同类型的实例可用的情况下,做区分。

image-20220830172806833

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);这里配置类2中的标签要写·  
        A a = (A) ac.getBean("a3");
        System.out.println(a);

​    A a2 = (A) ac.getBean("a3");
​    System.out.println(a2);

​    System.out.println(a == a2);//单例True,反之F

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.13.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>      

import xml

Spring xml文件中 import resource 是一种指令,用于导入其他的Spring配置文件,从而实现配置文件的分模块和复用。

你可以将数据库配置,事务管理,AOP切面等分别放在不同的配置文件中,然后在applicationContext.xml中使用如下代码导入。

防止单个文件太臃肿,类似.js文件的引入

毕设项目 对shiro.xml 的引用:

<!-- 引入shiro -->
        <import resource="shiro.xml" />

Spring AOP

背景:
  1. 有一个类Process,Process有三个动作,现在有需求:在步骤2前后加点什么,于是写出了Process2类,复制了Process的代码,添加了点东西,这时候又来需求...又写新建Process3,代码复用、重复工作严重。

  2. OOP的抽象,Process2、Process3 继承自 Process

  3. 静态代理: 不用继承,用代理,AbstractProcess作为父类,Process,Process2,Process3都从之继承,Process2, Process3重写 action1, action2, action3,但是干活的时候使用Process,Process2和Process3是代理类,Process真正干活。

  4. 静态代理还要将上面及几个类写好,现在基于反射实现动态代理,能即时创造一个代理类。 静态代理:在编写代码的时候就已经定义好,称为静态代理 动态代理:代理类的生成是在运行时临时的产生,比如使用反射,称为动态代理 这时候Process2,Process3就要退场了(要使用Process2、Process3InvocationHandler来生成动态代理类)。InvocationHandler必须要给接口,这时候创建接口IProcess.将抽象类中的方法复制到IProcess中并在接口中去掉这些方法的abstract修饰符,让抽象类实现这个接口

首先有这些步骤或动作,已经被重构到接口中了:IProcess

public interface IProcess {
    public void action1();
    public void action2() throws Exception ;
    public void action3();
    public int action4();
    public void action5(int i);
    public void action6(String i);
}

抽象类AbstractProcess继承了IProcess

public abstract class AbstractProcess implements IProcess {
    private IProcess process;//代理模式

	public IProcess getProcess() {
    	return process;
	}

	public void setProcess(IProcess process) {
    	this.process = process;
	}

	public static void doActions(IProcess process) throws Exception  {
    	process.action1();
    	process.action2();
    	process.action3();
    	process.action4();
    	process.action5(1);
    	process.action6("a");
	} 
}

Process类

import org.springframework.stereotype.Component;

@Component("process")
public class Process extends AbstractProcess {
    // 步骤1
    @Override
    public void action1() {
        System.out.println("action 1");
    }

// 步骤2
@Override
public void action2() throws Exception {
    System.out.println("action 2");
    System.out.println("action 2 again");

//        throw new Exception("abc");
    }

// 步骤3
@Override
public void action3() {
    System.out.println("action 3");
}
@Override
public int action4() {
    System.out.println("action 4");
    return 1;
}
@Override
public void action5(int i) {
    System.out.println("action 5");
}
@Override
public void action6(String i) {
    System.out.println("action 6");
}
}

Process2类看看即可:

package cn.liwang;

//public class Process2 extends Process {//OOP继承 public class Process2 extends AbstractProcess { // public void doActions() {//演示最初的代码复用 // // 步骤1 // System.out.println("action 1"); // // 步骤2 // System.out.println("before action 2"); // System.out.println("action 2"); // System.out.println("action 2 again"); // System.out.println("after action 2"); // // 步骤3 // } // System.out.println("action 3");

// @Override // public void action2() {//演示OOP继承时的代码,复习时不用看 // System.out.println("before action 2"); // super.action2(); // System.out.println("after action 2"); // }

@Override
public void action1() {
    this.getProcess().action1();
}

@Override
public void action2() throws Exception {
    System.out.println("before action 2");
    this.getProcess().action2();
    System.out.println("after action 2");
}

@Override
public void action3() {
    this.getProcess().action3();
}

@Override
public int action4() {
    System.out.println("action 4");
    return 1;
}

@Override
public void action5(int i) {
    System.out.println("action 5");
}

@Override
public void action6(String i) {
    System.out.println("action 6");
}
}

Process2InvocationHandler类,看看即可

package cn.liwang; //process2,3是代理类,干活的类是process import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;

public class Process2InvocationHandler implements InvocationHandler { private IProcess process;

public IProcess getProcess() {
    return process;
}

public void setProcess(IProcess process) {
    this.process = process;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object returnValue = null;

    // 通过调用的名称来判断
    if("action2".equals(method.getName())){//action2前后要加这些东西
        System.out.println("before action 2");
        returnValue = method.invoke(this.process, args);
        System.out.println("after action 2");
    } else {
        returnValue = method.invoke(this.process, args);
    }

    return returnValue;
}
}
实现:
//        Process2InvocationHandler handler = new Process2InvocationHandler();
//        handler.setProcess(process);

//        IProcess process2 = (IProcess) Proxy.newProxyInstance(process.getClass().getClassLoader(),
//                AbstractProcess.class.getInterfaces(),
//                handler);
//
//        AbstractProcess.doActions(process2);
        //为什么这样写,因为直接写process2.doactions时由于真正干活的类是process,action2输出:   System.out.println("action 2");  System.out.println("action 2 again");
//只有改写AbstractProcess.doActions,让它接收参数,才会输出process2中的action2:

//        Process3InvocationHandler handler = new Process3InvocationHandler();
//        handler.setProcess(process);
//
//        IProcess process3 = (IProcess) Proxy.newProxyInstance(process.getClass().getClassLoader(),
//                AbstractProcess.class.getInterfaces(),
//                handler);
//
//        AbstractProcess.doActions(process3);

Spring AOP

JOINPoint(连接点):比如每个action,就是可切分开的动作。连接点是程序执行过程中插入切面的地点,它实际上是对象的一个操作。

PointCut(切入点):比如process2中action2,在他前后加点东西。切入点是切面注入程序中的位置,切面是通过切入点被注入的。

通知Advice:切面的某个特定的连接点上执行的动作。

Target Object:目标对象,是被通知的对象,既可以是编写的类,也可以是需要添加指定行为的第三方类,AOP会注意目标对象的变动,并随时准备向目标对象注入切面。

AOP代理(AOP ):将通知应用到目标对象后创建的对象。

织入:把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象。

Logger类里面含有注解

package cn.liwang;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component("logger")//
@Aspect
**//@Aspect表示切面类,一把刀(提供通知方法)本例中毫无疑问是logger类**
public class Logger {
    public void printLog() {
        System.out.println("Logger say xxxx here.");
    }

public void printLog2() {
    System.out.println("Logger say xxxx2 here.");
}

@Around("action2()")
public Object processAdvice(ProceedingJoinPoint pjp) {//Processadvice方法,参数就是xml中要劫持的方法比如action2. Proceedingjointpoint,要用它是必须要用抛出异常的。有了它,就可以手动设置可以出现哪些通知,比如try中可以安排后置,catch中可以安排异常通知等等。

//        System.out.println("Logger around here.");
        System.out.println("Logger before action");
        Object rtValue = null;

​    try {
​        Object[] args = pjp.getArgs();
​        rtValue = pjp.proceed(args);
​        System.out.println("Logger after-returning action");
​    } catch (Throwable t) {
​        System.out.println("Logger after-throwing action");
​    } finally {
​        System.out.println("Logger after action");
​    }

​    return rtValue;

}

从这里开始:是为了演示注解后加上的:
@Pointcut("execution(void cn.liwang.Process.action2())")  只要想劫持某个方法,就得现写进来这里把process中的action2()搬过来了,只需要方法名,无需方法内容,注解要写。
private void action2(){}
//    @Before("action2()")
    public void printBefore() {
        System.out.println("Logger say Before here.");
    }

//    @AfterReturning("action2()")
    public void printAfterReturning() {
        System.out.println("Logger say after returning here.");
    }

//    @AfterThrowing("action2()")
    public void printAfterThrowing() {
        System.out.println("Logger say after throwing here.");
    }

//    @After("action2()")
    public void printAfter() {
        System.out.println("Logger say after here.");
    }
}

下面使用SpringAop实现:

 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.13.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>

方式一:xml

创建Spring的xml文件,文件头照例去复制。

然后将干活的类与切面类装配进去。

image-20220830230935652

然后使用标签进行AOP配置:

image-20220830231356650

详解上图如何配置:

aop:pointcut标签即切入点是谁:标签有两个属性:id随便,expression:

         ="execution(方法表达式)"
          <方法表达式>: public void cn.liwang.Process.action2()
              访问修饰符可以省略:void cn.liwang.Process.action2()
              返回值可以变成 *  :  * cn.liwang.Process.action2()
              方法名可以是通配符 *: void cn.liwang.Process.*()
              包名可以是通配符 *: 可以是void cn.liwang.*.*() 也可以:void *.*.*.*()有几个包写几个*
              如果不确定包的级别 *: void *..*() 就这样写,123都可以被运行,int类型就不行了
              我们还可以设定函数的参数: 基础类型:int
                                    类名: java.lang.String 写全
                                    适配任意参数,有或者没有: ..
              一般的写法,某一个类里所有的方法:* cn.liwang.Process.*(..) 方法任意,参数不限

aop:aspect标签,切面类:id随便命名,ref属性是提供advice的类,然后该标签内嵌入如aop:around通知类型标签:

method属性为切面类里的通知方法,pointcut-ref属性内容为aop:pointcut标签的 id属性

通知类型:

Advice类型            before 前置通知
                     after-returning 后置通知
                     after-throwing 异常通知,与后置通知只能运行一个
                     after 最终通知
                     around 环绕通知,本例只输出了logger advice,输出时没有输出action2方法,只出现了通知。 PointCut需要你自己手动调用,所有的其他四种通知都能但需要手动安排

image-20220830232850463

使用:

 ApplicationContext ac = new AnnotationConfigApplicationContext(bean.xml);
    IProcess process = (IProcess) ac.getBean("process");//process是代理类(AOP代理对象),虽然是以target放进去的,但运行时完成织入被修改成代理对象了
    Process.doActions(process);

为什么上面其它的切面注释掉只保留了环绕通知?:

ProceedingJoinPoint,要用它是必须要用抛出异常的。有了它,就可以手动设置可以出现哪些同通知,比如try中可以安排后置,catch中可以安排异常通知等等,这样可以替代单独设置后置异常通知等。实际用哪种好自己按需看情况详见logger类中public Object processAdvice方法


方式二:annotation

annotation :1.xml中process与logger由于都是正常bean所以只需各自添加@component()

2@Aspect:用于注释给切面类,本例子中是logger(因为他提供通知方法,process是被切的)

3.在xml文件中: <aop:pointcut id="action2" expression="execution(void cn.liwang.Process.action2())"/>

而用annotation:    @Pointcut("execution(void cn.liwang.Process.action2())")
                   private void action2(){}

没错上面两行代码就是为了用annotation加到Logger类中的 @Pointcut配置要劫持的类中的方法,下面跟方法名

这就是使用注解时的不同之处,切poingcut与advice都在同一个类中,所以此类会被@Aspect注解。

通知方法类

//    @Before("action2()")
    public void printBefore() {
        System.out.println("Logger say Before here.");
    }

//    @AfterReturning("action2()")
    public void printAfterReturning() {
        System.out.println("Logger say after returning here.");
    }

//    @AfterThrowing("action2()")
    public void printAfterThrowing() {
        System.out.println("Logger say after throwing here.");
    }

//    @After("action2()")
    public void printAfter() {
        System.out.println("Logger say after here.");
    }
}
注意

可以将上面几个annotation都注释掉,在方法public Object processAdvice上面添加注释@Around("action2()"):

详见logger类

@Around("action2()")
public Object processAdvice(ProceedingJoinPoint pjp)

image-20220429132123623

使用仍然用xml+注解:

    ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml");

​      Process process = (IProcess) ac.getBean("process");

​      Process.doActions(process);

下面是不用xml,用配置类

配置类

package cn.liwang;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = "cn.liwang")//自动扫描

@EnableAspectJAutoProxy//自动打开
//三件套相当于代替了xml
public class SpringConfiguration {
}

使用:

    ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    IProcess process = (IProcess) ac.getBean("process");

    Process.doActions(process);

JDBCtemplate

JDBC ,Spring的核心类之一

该类在内部已经处理数据库资源的建立和释放,并可以避免一些常见的错误,如关闭连接及抛出异常等。简化了编程JDBC时所需要的基础代码。该类还可以直接通过数据源的引用实例化,然后在服务中使用,也可以通过依赖注入的方式在容器中。

//        // 配置
//        DriverManagerDataSource ds = new DriverManagerDataSource();
//        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
//        ds.setUrl("jdbc:mysql://localhost:3306/db2?serverTimezone=UTC&allowMultiQueries=true");
//        ds.setUsername("root");
//        ds.setPassword("root");
//
//        // JDBCTemplate
//        JdbcTemplate jt = new JdbcTemplate();
//        jt.setDataSource(ds);

image-20220831161940708

Spring IoC控制的JDBCTemplate

image-20220831162141306

//        ApplicationContext ac = new ClassPathXmlApplicationContext("application1.xml");
//        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

//        JdbcTemplate jt = (JdbcTemplate) ac.getBean("jt");

//        jt.execute("INSERT INTO USER (USERNAME, BIRTHDAY, GENDER, ADDRESS)" +
//                "VALUES" +
//                "('aaaa', '1981-01-17', '男', 'xxxxxx')");

        // 3. JDBCTemplate 增删改查
        //    3.1 增删改

//        jt.update("INSERT INTO USER (USERNAME, BIRTHDAY, GENDER, ADDRESS)" +
//                "VALUES" +
//                "(?, ?, ?, ?)", "aaaa", "1981-01-17", "男", "xxxxxx");

//        jt.update("DELETE FROM USER WHERE ID = ?", 24);

//        jt.update("UPDATE USER SET USERNAME = ? WHERE ID = ?", "bbbb", 23);

        //     3.2 查询

//        class UserRowMapper implements RowMapper<User> {//RowMapper<T>接口提供方法将表中一条记录,装成类,并返回。
//
//            @Override
//            public User mapRow(ResultSet resultSet, int i) throws SQLException {//如果查询的记录能对的上,一个User.但是可以不用缺省提供有
//                User user = new User();
//                user.setId(resultSet.getInt("ID"));
//                user.setUsername(resultSet.getString("USERNAME"));
//                user.setBirthday(resultSet.getString("BIRTHDAY"));
//                user.setGender(resultSet.getString("GENDER"));
//                user.setAddress(resultSet.getString("ADDRESS"));
//                return user;
//            }
//        }

//        List<User> users = jt.query("SELECT * FROM USER WHERE USERNAME = ?", new UserRowMapper(),"aaaa");//自己写的
//        List<User> users = jt.query("SELECT * FROM USER WHERE USERNAME = ?", new BeanPropertyRowMapper<User>(User.class),"aaaa");//用Spring缺省提供的,上面类就可注释掉(前提数据对的上,别乱查)
//
//
//        for(User user: users){
//            System.out.println(user);
//        }

        // 3.3 查询返回整数

//        Long count = jt.queryForObject("SELECT COUNT(*) FROM USER", Long.class);
//        System.out.println(count);
使用jdbctemplate的事务

创建:本类就是提供切入点的类,先学习1xml文件,使用标签。

package cn.liwang;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class AccountService {
    @Autowired
    private JdbcTemplate jt;

public JdbcTemplate getJt() {
    return jt;
}

public void setJt(JdbcTemplate jt) {
    this.jt = jt;
}

@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void updateTransfer(Integer id1, Integer id2, Integer balance) {//转钱
    jt.update("UPDATE ACCOUNT SET BALANCE = BALANCE - ? WHERE ID = ?", balance, id1);//问号代表要填的参数    

//        int i = 5 / 0;
        jt.update("UPDATE ACCOUNT SET BALANCE = BALANCE + ? WHERE ID = ?", balance, id2);
    }
}
// 3.4 事务
//        AccountService accountService = ac.getBean("accountService", AccountService.class);
//
//        accountService.updateTransfer(1, 3, 300);

image-20220831221312456

image-20220831221541338

tx:advice 标签可以了理解为配置事务属性。
    <!-- 可以配置的属性:isolation:事务的隔离界别,DEFAULT,使用数据库的隔离级别
                      propagation:事务的传递行为:REQUIRED(缺省),如果没有事务,新建一个,如果有,使用现有的。
                                                SUPPORTS,如果没有事务,就按照非事务处理,如果有,使用现有的。
                      read-only:事务是否只读:增删改 true,查询 false
                      timeout:以秒为单位,默认是-1,不超时
                      rollback-for:指定回滚的异常的类型,比如X,X发生时回滚,不是X时不会滚。缺省:没有,此时任何异常都需要回滚。
                      no-rollback-for:指定不回滚的异常的类型,比如Y,Y发生时不会滚,不是Y时回滚。缺省:没有,此时任何异常都需要回滚。
 -->

image-20220831223250302

然后学习注解:注解加1.xml文件 告知扫描

image-20220831223539421

然后使用注解加这个配置类:代替1.xml

package cn.liwang;
//替换application1.xml
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@ComponentScan(basePackages = "cn.liwang")//扫描包
@EnableTransactionManagement//代替文件中transcationManager
public class SpringConfiguration {
    @Bean(name = "ds")
    public DataSource createDataSource() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/db2?serverTimezone=UTC&allowMultiQueries=true");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }

@Bean(name = "jt")
public JdbcTemplate createJdbcTemplate(DataSource ds){
    JdbcTemplate jt = new JdbcTemplate();
    jt.setDataSource(ds);

​    return jt;
}

@Bean(name = "transactionManager")
public PlatformTransactionManager createTransactionManager(DataSource ds) {
    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
    transactionManager.setDataSource(ds);

​    return transactionManager;
}

0303后十分钟
// 3.5 好事的王校长想知道TransactionManager具体是怎么工作的?指的是不去配置,直接Java代码中用,怎么用?(无SpringIoc)
        // 配置
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/db2?serverTimezone=UTC&allowMultiQueries=true");
        ds.setUsername("root");
        ds.setPassword("root");

        // JDBCTemplate
        final JdbcTemplate jt = new JdbcTemplate();
        jt.setDataSource(ds);
        
        // TransactionManager
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(ds);
        
        // TransactionTemplate
        TransactionTemplate tt = new TransactionTemplate();
        tt.setTransactionManager(transactionManager);
        
        tt.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                // 干活
                AccountService accountService = new AccountService();
                accountService.setJt(jt);

                accountService.updateTransfer(1, 3, 300);
                return null;
            }
        });

使用注解Transactional

image-20230905011942013

 <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.13.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.13.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.13.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.23</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>

image-20220831123538134

Spring旅游网站

学到了 mapper实现类可直接作为service类的实例字段(装配即可。不需要使用new)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值