一篇文章入门Spring

Spring

1、Spring入门(IOC)

首先要在pom文件里导入依赖

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>5.3.6</version>
</dependency>

创建一个实体类

package com.zxjava.pojo;

public class Hero {

	
    public Hero() {
    }

    public Hero(String name) {
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void show(){
        System.out.println("这位英雄是"+name);
    }
}

编写一个配置文件,把实体类注册到beans中,并给name属性一个值为 “小提莫” ,这里给属性赋值是通过Set方法赋值的

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="hero" class="com.zxjava.pojo.Hero">
        <property name="name" value="小提莫"/>
    </bean>

</beans>

编写一个测试类

import com.zxjava.pojo.Hero;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        Hero hero = (Hero) context.getBean("hero");

        hero.show();
    }
}

看一下输出结果

在这里插入图片描述

展示一下目录结构

在这里插入图片描述

总结

  • 之前创建一个对象通过new创建,现在这个对象是由Spring创建的,Spring创建对象的方式就是通过实体类的无参构造创建的
  • Hero对象的属性是由Spring容器设置的,是通过实体类的set方法注入的,如果把实体类的set方法去掉,运行测试类就会报错

2、依赖注入(DI)

DI不是IOC,DI是属于IOC的

依赖注入的方式大致分为2中,分别是Set注入和构造器注入

在上一小节Spring入门的就是通过set方法注入

2.1 、通过有参构造方法来创建

  • 根据index参数下标设置:

    在配置文件beans.xml中重写bean标签

    <bean id="hero" class="com.zxjava.pojo.Hero">
        <constructor-arg index="0" value="盖伦"/>
    </bean>
    
  • 根据参数名字设置

    <bean id="hero" class="com.zxjava.pojo.Hero">
        <constructor-arg name="name" value="轮子妈"/>
    </bean>
    
  • 根据参数类型设置

    <bean id="hero" class="com.zxjava.pojo.Hero">
        <constructor-arg type="java.lang.String" value="艾希"/>
    </bean>
    

2.2、Spring配置别名

  • 通过alias

    <alias name="hero" alias="superHero"/>
    
  • 通过name

    <bean id="hero"  name="superHero,superHero2" class="com.zxjava.pojo.Hero">
        <constructor-arg type="java.lang.String" value="艾希"/>
    </bean>
    

    name的功能比较好,在bean标签中如果没有id,那么name就是默认标识符,如果配置了id那么name就是别名,name可以设置多个别名,可以用逗号、分号、空格隔开

2.3、Set注入

Set注入会演示不同属性类型的注入方式,比如String、List、Map等等。

首先创建两个实体类

package com.zxjava.pojo;

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}


package com.zxjava.pojo;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class Student {
    private String name;

    private Address address;

    private String[] books;

    private List<String> hobbys;

    private Map<String, String> card;

    private Set<String> games;

    private String money;

    private Properties info;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobbys() {
        return hobbys;
    }

    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public String getMoney() {
        return money;
    }

    public void setMoney(String money) {
        this.money = money;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    public void show () {

            System.out.println("name=" + name
                    + ",address=" + address.getAddress()
                    + ",books="
            );

            for (String book : books) {
                System.out.println("<<" + book + ">>");
            }

            System.out.println("\n爱好:" + hobbys);

            System.out.println("card" + card);

            System.out.println("games" + games);

            System.out.println("money" + money);

            System.out.println("info" + info);

        }
    }

通过配置文件展示不同属性的注入方式

<bean id="address" class="com.zxjava.pojo.Address">
    <property name="address" value="北京"/>
</bean>

<bean id="student" class="com.zxjava.pojo.Student">
   <!--常量注入-->
   <property name="name" value="小王"/>

    <!--bean注入-->
    <property name="address" ref="address"/>

    <!--数组注入-->
    <property name="books">
        <array>
           <value>坏蛋是怎样炼成的</value>
           <value>三国演义</value>
            <value>水浒传</value>
         </array>
     </property>

     <!--List注入-->
     <property name="hobbys">
        <list>
            <value>打游戏</value>
            <value>唱歌</value>
         </list>
     </property>

     <!--List注入-->
     <property name="card">
        <map>
          <entry key="身份证" value="111111"/>
           <entry key="银行卡" value="222222"/>
         </map>
     </property>

    <!--Set注入-->
    <property name="games">
        <set>
           <value>lol</value>
           <value>cf</value>
        </set>
    </property>

    <!--Null注入-->
    <property name="money"><null/></property>

    <!--Properties注入-->
    <property name="info">
       <props>
          <prop key="姓名">小王</prop>
          <prop key="性别"></prop>
       </props>
    </property>
 </bean>

编写测试类查看运行结果

@Test
public void studentTest(){
     ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

     Student student = (Student) context.getBean("student");
        
      student.show();
}

在这里插入图片描述

2.4、p命名注入

p(properties)命名注入,依然要设置set方法

p命名注入需要在文件中加入约束文件

xmlns:p="http://www.springframework.org/schema/p"
<bean id="hero" class="com.zxjava.pojo.Hero" p:name = "瑞兹"/>

2.5 、c命名注入

c(Constructor)命名注入,依然要设置有参构造方法

c命名注入需要在文件中加入约束问价

xmlns:c="http://www.springframework.org/schema/c"
<bean id="hero" class="com.zxjava.pojo.Hero" c:name="小法师"/>

3、自动装配

回顾

首先创建3个实体类(人、猫、狗)

package com.zxjava.pojo;

public class Cat {
    public void giao(){
        System.out.println("喵喵喵~");
    }
}

package com.zxjava.pojo;

public class Dog {
    public void giao(){
        System.out.println("汪汪汪~");
    }
}
package com.zxjava.pojo;

import com.zxjava.pojo.Cat;
import com.zxjava.pojo.Dog;

public class Person {
    private String name;

    private Dog dog;

    private Cat cat;

    public Person() {
    }

    public Person(String name, Dog dog, Cat cat) {
        this.name = name;
        this.dog = dog;
        this.cat = cat;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", dog=" + dog +
                ", cat=" + cat +
                '}';
    }
}

编写配置文件beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cat" class="com.zxjava.pojo.Cat"/>
    <bean id="dog" class="com.zxjava.pojo.Dog"/>

    <bean id="person" class="com.zxjava.pojo.Person">
        <property name="name" value="小王"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>
</beans>

测试

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        Person person = (Person) context.getBean("person");

        person.getCat().giao();
        person.getDog().giao();
    }

输入结果

在这里插入图片描述

3.1、通过配置xml自动装配

  • byName方式

    只需修改配置文件的bean

    <bean id="person" class="com.zxjava.pojo.Person" autowire="byName">
        <property name="name" value="小王"/>
    </bean>
    

    如果把cat或dog的bean id修改了,就会报空指针

  • byType方式

    <bean id="person" class="com.zxjava.pojo.Person" autowire="byType">
        <property name="name" value="小王"/>
    </bean>
    

3.2、通过注解自动装配

(@Autowired @Qualifier @Resource)

首先在spring配置文件中引入context文件头

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

开启注解支持,并把实体类注册

<context:annotation-config/>

<bean id="cat" class="com.zxjava.pojo.Cat"/>
<bean id="dog" class="com.zxjava.pojo.Dog"/>
<bean id="person" class="com.zxjava.pojo.Person"/>

在person实体类中使用注解自动装配

@Autowired
private Dog dog;

@Autowired
private Cat cat;

@Autowired是通过类型自动装配的,不支持用名称装配,所以就有个@Qualifier,@Qualifier可以通过名称装配,但是@Qualifier不能单独使用

修改一下beans.xml

<bean id="cat1" class="com.zxjava.pojo.Cat"/>
<bean id="cat2" class="com.zxjava.pojo.Cat"/>
<bean id="dog1" class="com.zxjava.pojo.Dog"/>
<bean id="dog2" class="com.zxjava.pojo.Dog"/>

此时只是用@Autowired就会报错,因为cat和dog分别有两个一样的类型,所以此时就用到了@Qualifier,修改Person,见下:

    @Autowired
    @Qualifier(value = "dog1")
    private Dog dog;

    @Autowired
    @Qualifier(value = "cat1")
    private Cat cat;

除此之外还有**@Resource**,如果有指定的name属性则先会根据byName方式进行装配,若不成功,则会再根据byType的方式进行装配

@Resource(name = "dog1")
private Dog dog;

@Resource(name = "cat1")
private Cat cat;

4、使用注解开发

首先要在beans.xml中指定注解扫描包并且在spring配置文件中引入context约束

<context:component-scan base-package="com.zxjava.pojo"/>

现在不需要再beans.xml中注入bean了只需要在实体类上加上@Component(“xxx”)注解即可

package com.zxjva.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("hero")
public class Hero {

    @Value("提莫")
    private String name;



    @Override
    public String toString() {
        return "Hero{" +
                "name='" + name + '\'' +
                '}';
    }
}

@Component有3个衍生注解:

  • @Controller:用于web层
  • @Service:用于service层
  • @Repository:用于dao层

这3个衍生注解与@Component的作用一样

5、纯Java类进行配置

纯java类进行配置就不需要beans.xml了,需要创建一个配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config {

    //注册一个bean,这里的返回值就bean的类型,方法名就是bean的id
    @Bean
    public Hero hero(){
        return new Hero();
    }
}

创建一个实体类

import org.springframework.beans.factory.annotation.Value;

public class Hero {
    @Value("盖伦")
    private String name;

    @Override
    public String toString() {
        return "Hero{" +
                "name='" + name + '\'' +
                '}';
    }
}

进行测试

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mytest {

    @Test
    public void test(){
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

        Hero hero = (Hero) context.getBean("hero");

        System.out.println(hero.toString());
    }
}

6、静态代理

代理模式就类似于租房

在这里插入图片描述

  • 租房就是一个抽象角色(一般用接口实现)
  • 房东就是真实角色(被代理的角色)
  • 中介就是代理角色(代理真实角色后,一般会有一些其他操作)
  • 客户就是使用代理角色来实现一些操作

首先创建一个抽象角色(租房):

public interface Rent {
    
    public void rent();
}

创建一个真实角色(房东):

房东的动作只有一个,就是租房,而中介会多一些操作

public class Landlord implements Rent{
    @Override
    public void rent() {
        System.out.println("房东租房...");
    }
}

创建一个代理角色(中介):

中介代理了房东,会多选房、收中介费等其他操作

public class Proxy implements Rent{


    Landlord landlord;

    public Proxy() {
    }

    public Proxy(Landlord landlord) {
        this.landlord = landlord;
    }

    @Override
    public void rent() {
        chooseHouse();

        landlord.rent();

        moeny();
    }

    public void chooseHouse(){
        System.out.println("选房");
    }

    public void moeny(){
        System.out.println("收钱");
    }
}

创建一个客户:

public class Client {

    public static void main(String[] args) {
        Landlord landlord =new Landlord();

        Proxy proxy = new Proxy(landlord);

        proxy.rent();
    }
}

再举一个静态代理的例子加深理解

创建一个抽象角色

package moreAction;

public interface Service {
    void add();

    void delete();

    void update();

    void query();
}

创建一个真实角色

package moreAction;

public class ServiceImpl implements Service{
    @Override
    public void add() {
        System.out.println("增加");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

    @Override
    public void update() {
        System.out.println("修改");
    }

    @Override
    public void query() {
        System.out.println("查询");
    }
}

创建一个代理角色,在真实角色的每个方法中再添加一些其他操作:

package moreAction;

public class ServiceImplProxy implements Service{

    ServiceImpl serviceImpl;

    public ServiceImplProxy(ServiceImpl serviceImpl) {
        this.serviceImpl = serviceImpl;
    }

    @Override
    public void add() {
        log("add");
        System.out.println("增加");
    }

    @Override
    public void delete() {
        log("delete");
        System.out.println("删除");
    }

    @Override
    public void update() {
        log("update");
        System.out.println("修改");
    }

    @Override
    public void query() {
        log("query");
        System.out.println("查询");
    }

    public void log(String msg){
        System.out.println("执行了"+msg+"方法");
    }
}

创建一个用户:

package moreAction;

public class Client {
    public static void main(String[] args) {

        ServiceImpl service = new ServiceImpl();

        ServiceImplProxy serviceImplProxy = new ServiceImplProxy(service);

        serviceImplProxy.add();
        serviceImplProxy.delete();
        serviceImplProxy.update();
        serviceImplProxy.query();
    }

}

7、动态代理

动态代理与静态代理一样,静态代理的代理类是提前写好的,而动态代理的代理类是动态生成的。

动态代理还是以房东中介这个例子介绍:

首先创建一个租房的接口:

public interface Rent {

    void rent();
}

在创建一个真实角色(房东):

public class Landlord implements Rent {
    @Override
    public void rent() {
        System.out.println("房东租房...");
    }

}

接下来就要写一个增强类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LandlordInvocationHandler implements InvocationHandler {

    private Rent rent;

    public LandlordInvocationHandler(Rent rent) {
        this.rent = rent;
    }

    /**
     * invoke三个参数:
     *
     * proxy:就是代理对象,newProxyInstance方法的返回对象
     *
     * method:调用的方法
     *
     * args: 方法中的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("方法执行前...");
        Object invoke = method.invoke(rent,args);
        System.out.println("方法执行后...");
        return invoke;
    }
}

创建一个用户:

import java.lang.reflect.Proxy;

public class Client {

    public static void main(String[] args) {
        Landlord landlord = new Landlord();
        /**
         * newProxyInstance,方法有三个参数:
         *
         * newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
         *
         * loader: 用哪个类加载器去加载代理对象
         *
         * interfaces:动态代理类需要实现的接口
         *
         * h:动态代理方法在执行时,会调用h里面的invoke方法去执行
         *
         */
        Rent rent = (Rent) Proxy.newProxyInstance(landlord.getClass().getClassLoader(), landlord.getClass().getInterfaces(), new LandlordInvocationHandler(landlord));

        rent.rent();
    }

}

在加强类中还有Rent属性,为了使增强类更加通用,还有加深一下动态代理的理解,请看下面的例子:

创建一个Service接口

public interface Service {
    void add();

    void delete();

    void update();

    void select();
}

创建一个真实角色

public class ServiceImpl implements Service{
    @Override
    public void add() {
        System.out.println("增加方法");
    }

    @Override
    public void delete() {
        System.out.println("删除方法");
    }

    @Override
    public void update() {
        System.out.println("修改方法");
    }

    @Override
    public void select() {
        System.out.println("查询方法");
    }
}

编写一个增强类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ServiceInvocationHandler implements InvocationHandler {

    private Object target;

    public ServiceInvocationHandler(Object target) {
        this.target = target;
    }

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

        System.out.println("方法执行前...");

        Object invoke = method.invoke(target,args);

        System.out.println("方法执行后...");

        return invoke;
    }
}

编写一个app类,用于测试

public class App {

    public static void main(String[] args) {
        ServiceImpl serviceIml = new ServiceImpl();
        
/**
         * newProxyInstance,方法有三个参数:
         *
         * newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
         *
         * loader: 用哪个类加载器去加载代理对象
         *
         * interfaces:动态代理类需要实现的接口
         *
         * h:动态代理方法在执行时,会调用h里面的invoke方法去执行
         *
         */
        Service service = (Service) Proxy.newProxyInstance(serviceIml.getClass().getClassLoader(), serviceIml.getClass().getInterfaces(), new ServiceInvocationHandler(serviceIml));

        service.add();

        service.delete();
    }

}

8、AOP

AOP是面向切面编程,AOP与代理模式类似,利用AOP可以使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高了开发的效率。

了解以下名词(不好理解可根据代码理解):

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

导入一个依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

8.1、第一种实现方式

编写接口和实现类

public interface Service {

    void add();

    void delete();

    void update();

    void select();
}
public class ServiceImpl implements Service{
    @Override
    public void add() {
        System.out.println("增加方法");
    }

    @Override
    public void delete() {
        System.out.println("删除方法");
    }

    @Override
    public void update() {
        System.out.println("修改方法");
    }

    @Override
    public void select() {
        System.out.println("查询方法");
    }
}

写两个增强类,一个前置增强类,一个后置增强类

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeLog implements MethodBeforeAdvice {

    //method : 要执行的目标对象的方法
    //args : 被调用的方法的参数
    //Object : 目标对象
    @Override
    public void before(Method method, Object[] args, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"方法执行了...");
    }
}
import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {

    //returnValue 返回值
    //method被调用的方法
    //args 被调用的方法的对象的参数
    //target 被调用的目标对象
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"方法执行了,返回值是"+returnValue);
    }
}

在spring的文件中注册 , 并实现aop切入实现 , 注意导入约束

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="service" class="com.zxjava.demo.ServiceImpl"/>
    <bean id="beforeLog" class="com.zxjava.demo.BeforeLog"/>
    <bean id="afterLog" class="com.zxjava.demo.AfterLog"/>

    <!--aop的配置-->
    <aop:config>

        <!--切入点 expression(修饰词 返回值 类名 方法名 参数):表达式匹配要执行的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.zxjava.demo.ServiceImpl.*(..))"/>

        <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>

    </aop:config>
</beans>

编写一个测试类

import com.zxjava.demo.Service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        Service service = (Service) context.getBean("service");

        service.add();
    }
}

执行结果

在这里插入图片描述

8.2、第二种实现方式

自定义类来实现AOP

新建一个切入类

public class MyPointCut {

    public void before(){
        System.out.println("方法执行之前...");
    }

    public void after(){
        System.out.println("方法执行之后...");
    }
}

在beans.xml中配置

 <bean id="myPointCut" class="com.zxjava.demo.MyPointCut"/>
<aop:config>

    <aop:aspect ref="myPointCut">
        <aop:pointcut id="pointcut" expression="execution(* com.zxjava.demo.ServiceImpl.*(..))"/>
        <aop:before pointcut-ref="pointcut" method="before"/>
        <aop:after pointcut-ref="pointcut" method="after"/>
     </aop:aspect>

</aop:config>

测试结果

在这里插入图片描述

8.3、第三种实现方式

使用注解实现

首先创建一个切面类

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnoPointCut {

    @Before("execution(* com.zxjava.demo.ServiceImpl.*(..))")
    public void before(){
        System.out.println("======方法执行之前======");
    }

    @After("execution(* com.zxjava.demo.ServiceImpl.*(..))")
    public void after(){
        System.out.println("======方法执行之后======");
    }

    @Around("execution(* com.zxjava.demo.ServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");

        jp.proceed();

        System.out.println("环绕后");
    }
}

在beans.xml中注册并加上支持注解的配置

<bean id="annoPointCut" class="com.zxjava.demo.AnnoPointCut"/>

<aop:aspectj-autoproxy/>

9、Spring整合MyBatis

首先导入Spring和MyBatis都需要的包和配置静态资源过滤配置

<dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.20</version>
            </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>            <version>5.1.10.RELEASE</version>
</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

9.1、回顾MyBatis

提前展示一下要创建的目录结构:

在这里插入图片描述

配置MyBatis的核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

    <typeAliases>
        <package name="com.zxjava.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/zxjava?useSSL=false&amp;allowPublicKeyRetrieval=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="971214"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <package name="com.zxjava.dao"/>
    </mappers>


</configuration>

新建一个实体类

package com.zxjava.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;


@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private int id;
    private String name;
    private String password;
}

创建一个接口

package com.zxjava.dao;

import com.zxjava.pojo.User;

import java.util.List;
import java.util.Map;

public interface UserMapper {
    List<User> selectUser();
}

编写一个接口对应的映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.zxjava.dao.UserMapper">

    <select id="selectUser" resultType="user">
        select * from User
    </select>

</mapper>

测试类

import com.zxjava.dao.UserMapper;
import com.zxjava.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {

    @Test
    public void test() throws IOException {
        String resource = "mybatis-config.xml";

        InputStream in = Resources.getResourceAsStream(resource);

        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in);

        SqlSession sqlSession = build.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> users = mapper.selectUser();

        for (User user : users) {
            System.out.println(user);
        }

    }
}

9.2、整合方式一

引入MyBatis的核心配置文件beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

配置数据源,在Spring中配置了数据源,MyBatis核心配置文件的数据源配置信息就不需要了

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/zxjava?useSSL=false&amp;allowPublicKeyRetrieval=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
    <property name="username" value="root"/>
    <property name="password" value="971214"/>
</bean>

配置SqlSessionFactory并关联MyBatis

(在这里还可根据property标签配置设置、别名等)

<!--在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory的
    ,而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建


    SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource对象,
    它的配置方法和其它 Spring 数据库连接是一样的。


    一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径
    -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>

    <!--关联MyBatis-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

注册sqlSessionTemplate,关联sqlSessionFactory

(SqlSessionTemplate中没有set方法,只能通过构造器注入)

<!--SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,
    这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

编写一个实现类

package com.zxjava.dao;

import com.zxjava.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper{

    //sqlSession不用我们自己创建了,Spring来管理
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public List<User> selectUser() {

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        return mapper.selectUser();
    }
}

然后把这个实现类注册到beans.xml中

<bean id="userMapperImpl" class="com.zxjava.dao.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>
</bean>

测试

@Test
public void Spring_MyBatis_test(){

    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

    UserMapperImpl userMapperImpl = context.getBean("userMapperImpl", UserMapperImpl.class);

    List<User> users = userMapperImpl.selectUser();

    for (User user : users) {
        System.out.println(user);
    }

}

这样Spring和MyBatis就整合完成了,通过在beans.xml中配置数据源、SqlSessionFactory等,MyBatis中的配置可以省去(习惯把和留在MyBatis核心配置文件中)

9.3整合方式二

直接让实现类继承SqlSessionDaoSupport就可直接获得sqlSession,这样就不需要注册sqlSessionTemplate了

编写实现类

package com.zxjava.dao;

import com.zxjava.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{

    @Override
    public List<User> selectUser() {


        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

把实现类在beans.xml中注册

<bean id="userMapperImpl2" class="com.zxjava.dao.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

这样Spring和MyBatis就整合完成了。

10、声明式事务

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

在接口中新增一个增加和删除接口

void insertUser(User user);

void deleteUser(int id);

编写映射文件(这里故意把删除语句写错)

<insert id="insertUser" parameterType="com.zxjava.pojo.User">
    insert into user(id,name,password) values (#{id},#{name},#{password})
</insert>

<delete id="deleteUser" parameterType="int">
    deletes from user where id = #{id}
</delete>

编写实现类

package com.zxjava.dao;

import com.zxjava.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{


    @Override
    public List<User> selectUser() {
        User user = new User(4,"小李子","1212");

        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);

        mapper.insertUser(user);

        mapper.deleteUser(4);


        return mapper.selectUser();
    }

    @Override
    public void insertUser(User user) {

    }

    @Override
    public void deleteUser(int id) {

    }

}

编写测试类

 public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        UserMapper userMapperImpl = context.getBean("userMapperImpl", UserMapper.class);

        userMapperImpl.selectUser();
    }

在执行测试类之前,数据库中有3条数据,在执行测试类之后,即使执行时报错(删除语句故意写错),还会插入一条数据,这就像我们去银行取钱,钱取出来了,但是银行卡没有扣钱,所以我们的需求是,插入和删除同时成功或失败。

用Spring管理事务,首先导入约束

xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd

配置事务管理器

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

配置好事务管理器再去配置事务的通知

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
        <tx:method name="selectUser" propagation="REQUIRED"/>
        <!--            <tx:method name="*"/>-->
    </tx:attributes>
</tx:advice>

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

配置AOP,配置之前导入约束

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.zxjava.dao.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>

这样再去测试的话,如果删除没有成功,插入也不会成功,就实现了这些动作要么全部完成,要么全部不起作用。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值