spring

spring简介

Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!

Spring 的优点
1、Spring是一个开源的免费的框架(容器)!
2、Spring是一个轻量级的、非入侵式的框架!--不会改变原有代码
3、控制反转(IOC),面向切面编程(AOP)!--两个最大优点
4、支持事务的处理,对框架整合的支持!
总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!


springmvc是spring的一部分

Spring 拓展

现代化的Java开发!说白就是基于Spring的开发!

Spring Boot

一个快速开发的脚手架。
基于SpringBoot可以快速的开发单个微服务。
约定大于配置。

Spring Cloud

SpringCloud是基于SpringBoot实现的。
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用!
弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”

IOC-控制反转

ioc理论推导

1。先导入maven

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.7.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.7.RELEASE</version>
    </dependency>
</dependencies>

原有现状

userdao--接口

package com.cjj.dao;

public interface UserDao {
    void getUser();
}
userdaoimpl--接口实现
package com.cjj.dao;

public class UserDaoImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("默认获取用户数据");
    }

}

userservice-业务接口
package com.cjj.service;

public interface UserService {
    void getUser();
}
userserviceimpl--业务接口实现类
package com.cjj.service;

import com.cjj.dao.UserDao;
import com.cjj.dao.UserDaoImpl;
import com.cjj.dao.UserDaoMysqllmpl;

public class UserServiceImpl implements UserService {
    private UserDao userDao=new UserDaoMysqllmpl();//多态用法

    //如果用户需求变了,你就一直要修改这里的代码,这就是耦合性太高了,改变源代码是非常麻烦的事情
    @Override
    public void getUser() {
        userDao.getUser();

    }
}
test类
import com.cjj.dao.UserDaoImpl;
import com.cjj.service.UserService;
import com.cjj.service.UserServiceImpl;

public class MyTest {
    public static void main(String[] args) {
        //用户实际调用的是业务层,dao层不需要接触
        UserService userService = new UserServiceImpl();
        userService.getUser();

    }
}

缺点:根据用户需求,每增加一个dao实现类,都得去改service的代码,非常麻烦,耦合性太高

将userserviceimpl实现类改成这样的代码,后面不管加了什么dao实现层,只需要改test类,不需要再修改userservice的代码
package com.cjj.service;

import com.cjj.dao.UserDao;
import com.cjj.dao.UserDaoImpl;
import com.cjj.dao.UserDaoMysqllmpl;

public class UserServiceImpl implements UserService {
 private UserDao userDao ;
 //利用set方法进行动态实现值的注入
     public void setUserDao(UserDao userDao) {
     this.userDao = userDao;
         }


    @Override
    public void getUser() {
        userDao.getUser();

    }
}

test类
import com.cjj.dao.UserDaoImpl;
import com.cjj.dao.UserDaoMysqllmpl;
import com.cjj.service.UserService;
import com.cjj.service.UserServiceImpl;

public class MyTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        ((UserServiceImpl)userService).setUserDao(new UserDaoMysqllmpl());//将userservice父类强转为子类userserviceimpl
        userService.getUser();


    }
}
解释为什么需要强转,父类接口只能使用子类实现类原有方法,而不能使用子类新定义的方法,要使用新定义的方法,就只能强转为子类

userService 变量的声明类型是 UserService,而实际创建的对象类型是 UserServiceImpl。UserService 是一个接口,而 UserServiceImpl 是这个接口的一个实现类。setUserDao 方法是定义在 UserServiceImpl 类中的,而不是在 UserService 接口中。

因此,如果你想调用 UserServiceImpl 中的 setUserDao 方法,就需要将 userService 变量强转为 UserServiceImpl 类型。这是因为通过接口类型的变量,只能访问该接口中定义的方法,无法访问实现类中特有的方法。

在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价十分昂贵!

核心
我们使用一个Set接口实现,已经发生了革命性的变化!

UserDao userDao;

public void setUserDao(UserDao userDao){
    this.userDao = userDao;
}

之前,程序是主动创建对象!控制权在程序员手上!
使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!
控制反转了,主动权交给用户了
这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了。系统的耦合性大大降低~,可以更加专注的在业务的实现上!这是IOC的原型!

IOC本质

原来加了dao层还要去修改业务层,通过这个IOC方式,让前端传个名字给到接口就可以去调对应的接口


控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

入门程序

1,写一个实体类

package com.cjj.pojo;

public class Hello {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

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

2、配置文件

<?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">
  <!--使用spring来创建对象,在spring这些都称为bean-->
    <bean id="hello" class="com.cjj.pojo.Hello">
        <property name="str" value="Spring"/>
    </bean>
    </beans>

3、测试

import com.cjj.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        //获取spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象都在spring的上下文中,通过getBean方法获取对象
        Hello hello= (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}
//输出结果:Hello{str='hello world'}

思考问题?
Hello对象是谁创建的?
Hello对象是由Spring创建的。

<?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">

    <!--使用Spring 来创建对象,在Spring 中,这些都称为bean
    类型  变量名 = new 类型();
    User user = new User();

   
    id = 变量名
    class = new 的对象;
    property 相当于 给对象中的属性设置一个值!

    -->
    <bean id="user" class="cn.bloghut.domain.User">
            <property name="name" value="csdn_闲言"/>
    </bean>

</beans>

Hello对象的属性是怎么设置的?
Hello对象的属性是由Spring容器设置的。

这个过程就叫控制反转:

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。

反转:程序本身不创建对象,而变成被动的接收对象。

依赖注入:就是利用set方法来进行注入的。

IOC是一种编程思想,由主动的编程变成被动的接收。

可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。
用ctrl+alt+U能看到这个类的完整源码继承
到了现在,不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!
将前面IOV理论推导的案例代码用spring后,直接用配置文件创建对象

加一个配置文件
<?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="mysqlImpl" class="com.cjj.dao.UserDaoMysqllmpl"/>
    <bean id="Impl" class="com.cjj.dao.UserDaoMysqllmpl"/>
    <bean id="UserServiceImpl" class="com.cjj.service.UserServiceImpl">
        <!--
        ref:引用spring容器中创建好的对象
        value:直接赋值,一般用于简单类型数据
        -->
            <property name="userDao" ref="mysqlImpl"/>
    </bean>


</beans>
test用成spring取对象
import com.cjj.dao.UserDaoImpl;
import com.cjj.dao.UserDaoMysqllmpl;
import com.cjj.service.UserService;
import com.cjj.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        //获取spring的上下文对象,拿到spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //容器在手,可以直接从容器中获取bean对象
        UserServiceImpl  UserServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");
        UserServiceImpl.getUser();

    }
}

IOC创建对象的方式

11、使用无参构造创建对象,默认!

user类
package com.cjj.pojo;

public class User {
    private String name;
    //默认是有无参构造
    public User() {
        System.out.println("User的无参构造");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public void show(){
        System.out.println("name"+name);
    }
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--使用spring来创建对象,在spring这些都称为bean-->
    <bean id="user" class="com.cjj.pojo.User">
        <property name="name" value="cjj"/>
    </bean>
</beans>

test类
public class MyTest {
    public static void main(String[] args) {
        //获取spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象都在spring的上下文中,通过getBean方法获取对象
       User user= (User) context.getBean("user");

    }
}

2、有参构造创建对象
user类
package com.cjj.pojo;

public class User {
private String name;
//默认是有无参构造
public User(String name) {
this.name = name;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}
public void show(){
    System.out.println("name"+name);
}

}

xml配置
<!-- 第一种,下标赋值 -->
  <bean id="user" class="com.cjj.pojo.User">
    <constructor-arg index="0" value="cjj"/>
    </bean>

 <!-- 第二种,类型 -->
  <bean id="user" class="com.cjj.pojo.User">
    <constructor-arg type="java.lang.String" value="cjj"/>
    </bean>

 <!-- 第三种,参数名 ,最常用!!-->
  <bean id="user" class="com.cjj.pojo.User">
    <constructor-arg name="name" value="cjj"/>
    </bean>
**总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!**
**在getbean时,会把beans.xml中的拥有的对象都创建了**
 //获取spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象都在spring的上下文中,通过getBean方法获取对象
       User user= (User) context.getBean("user");
       user.show();

Spring 容器,就类似于婚介网站。

信息都注册在里面
你想查看(获取)的时候再拿

spring配置

别名

<?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="user" class="com.cjj.pojo.User">
    <constructor-arg name="name" value="cjj"/>
    </bean>
    <alias name="user" alias="aa"/>

</beans>
public class MyTest {
    public static void main(String[] args) {
        //获取spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象都在spring的上下文中,通过getBean方法获取对象
       User user= (User) context.getBean("aa");
       user.show();

    }
}

bean的配置

   <!--
   id: bean的唯一标识符,相当于我们学的对象名
   class: bean对象对应的全限定名:包名+类名
name: bean的别名,可以多个别名,用逗号,分号隔开
   -->
  <bean id="user" class="com.cjj.pojo.User" name="cjj">
    <constructor-arg name="name" value="cjj"/>
    </bean>
    <alias name="user" alias="aa"/>

</beans>

import

一般用于团队开发使用,可以将多个配置文件,导入合并成一个目标
假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="beans.xml"/>
    <import resource="beans2.xml"/>
  <bean id="user" class="com.cjj.pojo.User" name="cjj">
    <constructor-arg name="name" value="cjj"/>
    </bean>
    <alias name="user" alias="aa"/>

</beans>

依赖注入

依赖:bean对象的创建依赖地容器
注入:bean对象的所有属性,由容器来注入

构造器注入

参考上面的IOC创建对象的方式,说的就是通过构造器注入(包含有参构造器和无参构造器)

set方式注入(重点)

student类

package com.cjj.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    public String getName() {
        return name;
    }
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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="address" class="com.cjj.pojo.Address">
        <property name="address" value="csdn_闲言"/>
    </bean>

    <bean id="student" class="com.cjj.pojo.Student">
        <!--注入address 类型-->
        <property name="address" ref="address"/>
        <!--String 类型-->
        <property name="name" value="csdn_闲言"/>
        <!--String 类型-->
        <property name="books">
            <array>
                <value>JavaSE</value>
                <value>JavaWeb</value>
                <value>Spring</value>
                <value>SpringMVC</value>
                <value>Mybatis</value>
            </array>
        </property>
        <!--List-->
        <property name="hobbies">
            <list>
                <value>唱</value>
                <value>跳</value>
                <value>rap</value>
                <value>篮球</value>
            </list>
        </property>
        <!--Map-->
        <property name="card">
            <map>
                <entry key="闲言" value="csdn——闲言"></entry>
                <entry key="闲言博客" value="csdn——闲言——博客"></entry>
            </map>
        </property>
        <!--set-->
        <property name="games">
            <set>
                <value>唱</value>
                <value>跳</value>
                <value>rap</value>
                <value>篮球</value>
            </set>
        </property>
        <!--String-->
        <property name="wife" value="xxx"></property>
        <!--Properties-->
        <property name="info">
            <props>
                <prop key="p1">v1</prop>
                <prop key="p2">v2</prop>
                <prop key="p3">v3</prop>
            </props>
        </property>
    </bean>

</beans>
test类
mport com.cjj.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student)context.getBean("student");
        System.out.println(student.getName());
    }

}
//输出student{name='csdn_闲言', address=com.cjj.pojo.Address@7b227d8d, books=[JavaSE, JavaWeb, Spring, SpringMVC, Mybatis], hobbies=[唱, 跳, rap, 篮球], card={闲言=csdn——闲言, 闲言博客=csdn——闲言——博客}, games=[唱, 跳, rap, 篮球], wife='xxx', info={p1=v1, p2=v2, p3=v3}}

拓展方式注入

可以使用p命名空间c命名空间进行注入
p 命名空间对应 setter 方式注入(要提供set方法)
c 命令空间对应 构造方法 (要提供有参构造方法)

pojo
public class User {
    private String name;
    private int age;
    public User() {
    }
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
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">
    <!-- xmlns:p="http://www.springframework.org/schema/p"-->
    <!--xmlns:c="http://www.springframework.org/schema/c"-->

    <!--P命名空间,p:name和p:age属性值将会被设置到User类的name和age属性上-->
    <bean id="user" class="com.cjj.pojo.User" p:name="John" p:age="25"/>
    <!--C命名空间,通过构造器注入:constructor-arg标签-->
    <bean id="user2" class="com.cjj.pojo.User" c:name="Mike" c:age="30"/>

</beans>
test
import com.cjj.pojo.Student;
import com.cjj.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("userbean.xml");
        User user = context.getBean("user2", User.class);
        System.out.println(user.getName());


    }
}

但是上面案例有点问题,无参构造的可以注入,有参构造注入报错,先不管了,后面有时间再研究

Bean的作用域


1、单例模式(Spring 默认机制)-唯一对象--一般用于单线程

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("userbean.xml");
        User user = context.getBean("user", User.class);
        User user2 = context.getBean("user", User.class);
        System.out.println(user == user2);//true


    }
}

配置文件
<bean id="user" class="com.cjj.pojo.User" p:name="John" p:age="25" scope="singleton"/>

2、原型模型:每次从容器get时,都会产生一个新对象--一般用于多线程
<bean id="user" class="com.cjj.pojo.User" p:name="John" p:age="25" scope="prototype"/>

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("userbean.xml");
        User user = context.getBean("user", User.class);
        User user2 = context.getBean("user", User.class);
        System.out.println(user.hashCode());//1519736165
        System.out.println(user2.hashCode());//1653844940


    }
}

3、其余的request,session,appliction,这些只能在web开发中使用

Bean的自动装配

自动装配是Spring 满足bean依赖的一种方式!
Spring 会在上下文自动寻找,并自动给bean 装配属性!

在Spring 中有三种装配的方式

1、在xml 中显示的配置--前面都是xml显示配置
2、在java中显示配置--后面学
3、隐式 的自动装配bean【重要】

测试

pojo,一个人有两个宠物

public class People {
    private Cat cat;
    private Dog dog;
    private String name;
猫类
package com.cjj.pojo;

public class Cat {
    public void shout() {
        System.out.println("miao miao miao");
    }
}
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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cat" class="com.cjj.pojo.Cat"/>
    <bean id="dog" class="com.cjj.pojo.Dog"/>
  
    <bean id="people" class="com.cjj.pojo.People">
        <property name="name" value="cjj"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>


</beans>
test类
import com.cjj.pojo.Cat;
import com.cjj.pojo.People;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        People people= context.getBean("people", People.class);
        people.getCat().shout();// miao miao miao

    }
}

ByName自动装配


   <bean id="cat" class="com.cjj.pojo.Cat"/>
    <bean id="dog" class="com.cjj.pojo.Dog"/>
  
    <bean id="people" class="com.cjj.pojo.People">
        <property name="name" value="cjj"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>

改成自动装配

    <bean id="cat" class="com.cjj.pojo.Cat"/>
    <bean id="dog" class="com.cjj.pojo.Dog"/>
    <!--
      byType:会自动在容器上下文中查找,和自已对象set方法后面的值对应的beanid
     byType:会自动在容器上下文(指xml文件)中查找,和自己**对象属性**类型相同的bean,注意是属性:对象--看的是class="com.cjj.pojo.Dog
 -->
    <bean id="people" class="com.cjj.pojo.People" autowire="byType">
        <property name="name" value="cjj"/>

    </bean>

byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
**  byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致**

使用注解实现自动装配

1、xml文件开启注解支持

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:context="http://www.springframework.org/schema/context"
      xmlns:aop="http://www.springframework.org/schema/aop"
      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
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">

   <!--开启注解支持-->
   <context:annotation-config/>
</beans>

@Autowired

1、在属性上使用--在属性上写后,set方法都可以不用写
2、在set方式上使用
3、使用Autowired 可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在(需要通过其他方式注入进容器),且符合名字byName。--代替了xml中的byName

甚至set方法都不用写了,已经实现了将cat,dog注入到属性中
public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

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

    public Dog getDog() {
        return dog;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
bean.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:context="http://www.springframework.org/schema/context"
       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.cjj.pojo.Cat"/>
    <bean id="dog" class="com.cjj.pojo.Dog"/>
    <bean id="people" class="com.cjj.pojo.People" >
  

    </bean>


</beans>

补充,如果 @Autowired(required=false),说明这个对象可以为null,也不会报错

@Qualifier

当我们的容器存在多个相同类型,不同名称的bean。使用@Autowired 无法完成自动装配了
这个时候需要使用@Qualifier 和@Autowired 注解一起使用。
使用@Qualifier 指定一个唯一的bean对象注入!

public class People {
    @Autowired
    @Qualifier(value = "cat11")
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
   <bean id="cat11" class="com.cjj.pojo.Cat"/>
    <bean id="cat22" class="com.cjj.pojo.Cat"/>
    <bean id="dog" class="com.cjj.pojo.Dog"/>
    <bean id="people" class="com.cjj.pojo.People" >

@Resource--jdk原有的,但是jdk11要导入这个注解包,一般还是aotowired注解

1、不指定name值,先去判断byName和byType,有一个能注入即成功

2、根据类型和id查找
如果IOC容器中存在多个不同名称,相同类型的bean,可以通过@Resource注解的name属性指定唯一的id;

总结:

1、都是用来自动装配的,都可以放在属性字段
2、@Autowired通过byType(类型)的方式实现,而且必须要求这个对象存在!
3、@Resource默认通过byName(id)的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!
执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byName的方式实现。

spring注解开发

在Spring4之后,要使用注解开发,必须保证aop的包导入了
使用注解需要导入context约束,增加注解的支持!

component组件注解的用法

   <!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.cjj.pojo"/>
    <context:annotation-config/>
user类
package com.cjj.pojo;

import org.springframework.stereotype.Component;
//等价于<bean id="user" class="com.cjj.pojo.User"></bean>
//加了@component组件就等价了上面
@Component
public class User {
    public String name="cjj";
}
<?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:context="http://www.springframework.org/schema/context"
       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:component-scan base-package="com.cjj.pojo"/>
    <context:annotation-config/>
</beans>
test类
import com.cjj.pojo.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user= context.getBean("user", User.class);
        System.out.println(user.name);

    }
}

属性注入使用@Value注解

package com.cjj.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//等价于<bean id="user" class="com.cjj.pojo.User"></bean>
//加了@component组件就等价了上面
@Component
public class User {
    @Value("张三")
    public String name;
}

或注入到set方法内

package com.cjj.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//等价于<bean id="user" class="com.cjj.pojo.User"></bean>
//加了@component组件就等价了上面
@Component
public class User {

    public String name;
    @Value("张三")
    public void setName(String name) {
        this.name = name;
    }
}

衍生注解

@Componet有几个衍生注解,在web开发中,会按照mvc三层架构分层!
@Service--------业务层注解
@Repository—持久层注解
@Controller-----控制层注解
在对应的层加上这个注解,就会在xml文件加上//等价于
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean

小结:
总结

XML 与 注解

xml更加万能,适用于任何场合!维护简单方便
注解不是自己类使用不了, 维护相对复杂
XML 与 注解最佳实践

xml用来管理bean
注解只负责完成属性的注入
我们在使用过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持

使用JavaConfig实现配置-代替xml

完全不使用spirng的xml配置,全权交给java来做
javaconfig是spring的一个子项目,在spring4后,变成核心功能
1、写一个config类,注意后面每增加一个bean,就要写一个config类,然后集中import到一个config中,后面test类就引用那个config类

package com.cjj.config;

import com.cjj.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//这个也会被sprng容器托管,注册到容器中,因为他本来就是一个@Component,就是一个配置类,相当于xml
@Configuration
public class CjjConfig {
    //注册一个bean,就相当于我们之前写了一个bean标签
    //这个方法的名字就是bean的id
    //这个方法的返回值就是bean的类型
    @Bean
    public User getUser() {
        return new User();//就是返回要注入到bean的对象
    }
}

2、User类

package com.cjj.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//这个也会被sprng容器托管,注册到容器中
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }
@Value("cjj")
    public void setName(String name) {
        this.name = name;
    }

3、test类

import com.cjj.config.CjjConfig;
import com.cjj.pojo.User;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        //如果 完全使用配置类方式去做,AnnotationConfigApplicationContext
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CjjConfig.class);
        User user = context.getBean("getUser", User.class);
        System.out.println(user);
    }
}

纯Java的配置方式,在springboot随处可见

使用component组件就是被sprng容器托管,注册到容器中,这个话是重点!包括config也是相当于组件 ,注册到容器

小结下上面的内容

Spring 容器和 XML 的关系,可以理解为“清单”和“执行者”的关系

XML 文件就像是一份“组件清单”,详细记录了要创建哪些对象(Bean)、对象之间的依赖关系、配置参数等。
Spring 容器则是“执行者”,它读取这份 XML 清单,根据规则自动创建对象、组装依赖,最终管理整个程序中的组件。
甚至可以完全不用xml文件,完全用注解也行,但是要配置一个config类

XML 在 Spring 容器中的核心作用
定义 Bean
用 XML 明确告诉容器要创建哪些对象,比如:

xml

<!-- 定义一个名为 userService 的 Bean,对应的类是 UserService -->
<bean id="userService" class="com.example.UserService" />
配置依赖注入
描述对象之间的依赖关系,比如 UserService 需要 UserDao:

xml

<bean id="userService" class="com.example.UserService">
    <!-- 将名为 userDao 的 Bean 注入到 userService 中 -->
    <property name="userDao" ref="userDao" />
</bean>

<bean id="userDao" class="com.example.UserDao" />
管理生命周期和配置
比如初始化方法、销毁方法、作用域(单例/多例)等:

xml

<bean id="dataSource" class="com.example.DataSource"
      init-method="connect"
      destroy-method="close"
      scope="singleton" />

🔄 Spring 容器如何处理 XML?
加载 XML:容器启动时,读取 XML 文件(如 applicationContext.xml)。
解析配置:根据 XML 中的 定义,确定要创建哪些对象及依赖关系。
创建 Bean:通过反射(Reflection)技术,按配置实例化对象。
注入依赖:将依赖的 Bean(如 UserDao)自动注入到需要的地方(如 UserService)。

🌰 举个栗子:XML vs 现代注解
传统 XML 配置(过去的主流方式):

xml

<!-- 全部依赖写在 XML 中 -->
<bean id="userService" class="com.example.UserService">
    <property name="userDao" ref="userDao" />
</bean>
<bean id="userDao" class="com.example.UserDao" />

现代注解配置(现在更常用):

java

@Component // 直接标记为 Bean,无需 XML
public class UserService {
    @Autowired // 自动注入 UserDao
    private UserDao userDao;
}

@Component
public class UserDao { ... }

💡 XML 的现状与替代方案
历史地位:早期 Spring 的核心配置方式是 XML,所有 Bean 和依赖均需手动编写。
现代趋势:Spring Boot 兴起后,注解(如 @Component、@Autowired)和 Java 配置成为主流,XML 逐渐简化或弃用。
共存场景:部分复杂配置(如数据库连接池、第三方库集成)仍可能用 XML 或混合配置。
📌 总结
Spring 容器是管理和组装对象的核心引擎。
XML是传统上控制容器的“配置说明书”,注解是现代的“快捷指令”。
你可以用 XML,也可以用注解,甚至混合使用——容器最终会根据配置执行动作!

IoC:解决对象创建和依赖管理的问题,让代码更解耦。
AOP:解决横向关注点重复代码的问题,让逻辑更清晰。
关系:IoC 创造对象,AOP 修饰对象,二者结合让 Spring 成为高效的代码管家!

AOP

代理模式

中介
为什么要学习代理模式?因为是springAOP的底层
代理模式的分类:
--静态代理
--动态代理

静态代理

角色分析:
1、抽象角色:一般会使用接口或者抽象类来解决(抽象下面三个角色)
2、真实角色:被代理的角色
3、代理角色:代理 真实角色,代理真实角色后,我们一般会做一些附属操作
4、客户:访问代理对象的人

1、创建租房接口
package com.cjj.demo01;

public interface Rent {
    public void rent();
}
2、房东实现租房接口
package com.cjj.demo01;

public class Host implements Rent {
    public void rent() {
        System.out.println("我是房东要出租房子了");
    }
}
3、代理实现租房接口,顺便写一些其他方法
package com.cjj.demo01;

public class Proxy implements Rent {
    private Host host;
    public Proxy() {
    }
    public Proxy(Host host) {
        this.host = host;
    }
    public void rent() {
        host.rent();
        lookHouse();
        signContract();
    }
    //中介带你看房
    public void lookHouse() {
        System.out.println("中介带你看房");
    }
    //中介带你签合同
    public void signContract() {
        System.out.println("中介带你签合同");
    }
}
4、租客
package com.cjj.demo01;

public class Client {
    public static void main(String[] args) {
        // 房东出租房子
        Host host = new Host();
        //代理,中介除了帮房东租房子,还要一些附属操作
        Proxy proxy = new Proxy(host);
        //你不用面对房东,直接找中介租房
        proxy.rent();

    }
}

代理模式的好处:
1、可以使真实角色的操作更加存粹!不用去关注一些公共的业务
2. 公共交给了代理角色,实现了业务的分工
公共业务发生扩展的时候,方便集中管理
缺点:
一个真实角色就会产生一个代理角色,代码量会翻倍 开发效率变低

再用一个案例加深:
业务接口

public interface UserService {
    void add();

    void delete();

    void update();

    void query();
}
业务类实现public class UserServiceImpl implements UserService {
    @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("查询用户");
    }
}
代理类
public class UserServiceProxy implements UserService {

    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

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

    //日志方法
    public void log(String msg){
        System.out.println("【Debug 】使用了"+msg+"方法");
    }
}
测试类
public static void main(String[] args) {

    UserServiceProxy userService = new UserServiceProxy();
    userService.setUserService(new UserServiceImpl());
    userService.add();
}

有一天,公司领导要求我为 某个类的所有方法新增日志输出功能。
怎么做呢?
在原来的类上的每一个方法添加日志输出?--在业务代码加上日记功能,大忌
这就是改变原来人家的代码了。
改动原有的业务代码,在公司中是大忌!
有可能人家你修改了人家的代码,可能给改蹦了。
新增一个类,制作成本小,职责单一。(代理类)


动态代理--这里是java原生的类就能实现了,和spring没关系

1、动态代理和静态代理角色一样
2、动态代理的代理类是动态生成的,不是我们直接写好的
3、动态代理分为两大类:基于接口的动态代理,基于类的动态代理
--基于接口:JDK动态代理
--基于类: cglib
--java字节码实现: javasist
需要了解两个类:Proxy :代理 InvocationHandler:调用处理程序

1、写一个类,实现动态代理的功能--proxy与invocationhandler

package com.cjj.demo01;

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

// 我们会用这个类,生成代理
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Rent rent;
    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成代理类
    public Object getProxy() {
       return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }
    @Override
    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        //动态代理的本质 ,就是使用反射机制
        Object result = method.invoke(rent, args);
        fare();
        return result;
    }
    public void seeHouse() {
        System.out.println("看房子");
    }
    public void fare(){
        System.out.println("交租金");
    }
}

现在就不用写代理类,直接用上面的类

package com.cjj.demo01;

public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        //代理角色:现在没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        pih.setRent(host);
       Rent proxy =(Rent) pih.getProxy();//这里的proxy就是动态生成的
       proxy.rent();

    }
}

基于Proxy类和InvocationHandler 实现动态代理 2
Proxy:生成动态代理实例的
InvocationHandler:调用处理程序并返回结果的

万能的自动生成代理类-后期只要传入参数,就根据实际情况生成代理类

package cn.bloghut.demo3;

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

/**
 * @Classname ProxyInvocationHandler
 * @Description 自动生成代理类
 * @Date 2021/5/17 15:44
 * @Created by 闲言
 */
public class ProxyInvocationHandler implements InvocationHandler {

    //1.被代理的接口
    public Object target;

    public void  setTarget(Object target){
        this.target = target;
    }

    //2.生成得到代理类(代理谁)
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    //3.处理代理实例,并返回结果(代用代理程序)
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态获取方法名称
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }
    /**
     * 打印日志方法
     * @param msg
     */
    public void log(String msg){
        System.out.println("[debug]===> 执行可"+msg+"方法");
    }
}
public static void main(String[] args) {

    //真实角色
    UserServiceImpl userService = new UserServiceImpl();
    //代理角色,不存在
    ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler();
    //设置要代理的对象
    invocationHandler.setTarget(userService);
    //获取代理对象
    UserService proxy = (UserService)invocationHandler.getProxy();
    //执行方法
    proxy.query();
}
//[debug]===> 执行可query方法
//query

动态代理的好处
1、使真实角色的操作更加纯粹,不用去关注一些公共业务
2、公共就交给代理角色,实现了业务分工
3、公共业务发生扩展时候,方便集中管理
4、一个动态代理类代理一个接口,一般就是对应的一类业务
5、一个动态代理类可以代理多个类,只要是实现同一个接口

spring-AOP实现方式--前面只是说AOP的原理和实现逻辑,这里是开始说springr的AOP怎么做

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

实现方式1:基于springAPI

导包
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

1、建立service接口和实现类

package com.cjj.service;

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}


package com.cjj.service;

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加了一个用户");
    }
    public void update() {
        System.out.println("更新了一个用户");
    }
    public void select() {
        System.out.println("查询了一个用户");
    }
    public void delete() {
        System.out.println("删除了一个用户");
    }

}

2、插入的类--日记

package com.cjj.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
    //method:要执行目标对象的方法
    //args:方法的参数
    //target:目标对象
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + "->" + method.getName() + "() start");
    }
}
package com.cjj.log;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
//returnValues:返回值
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回值为:"+returnValue);
    }
}

3、写配置文件

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--注册bean-->
    <bean id="userService" class="com.cjj.service.UserServiceImpl"/>
    <bean id="afterLog" class="com.cjj.log.AfterLog"/>
    <bean id="log" class="com.cjj.log.Log"/>
    <!--方式一:使用原来spring API 接口-->
<!--配置AOP,上面会导入aop命名空间,需要导入aop的约束-->
    <aop:config>
        <!--切入点:express:表达式:execution(要执行的位置!***)-->
        <aop:pointcut id="pointcut" expression="execution(* com.cjj.service.UserServiceImpl.*(..))"/>
        <!--执行环绕通知-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
        <!--通知:before:前置通知,after:后置通知,after-returning:返回通知,after-throwing:异常通知-->

    </aop:config>

4、test

import com.cjj.service.UserService;
import com.cjj.service.UserServiceImpl;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applcationContext.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}
//com.cjj.service.UserServiceImpl->add() start
//增加了一个用户
//执行了add方法,返回值为:null

方式二:自定义类来实现AOP

1、写一个自定义类

package com.cjj.diy;

public class DiyPointCut {
    public void before() {
        System.out.println("======方法执行前====");
    }
    public void after() {
        System.out.println("======方法执行后====");
    }
}

2、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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--注册bean-->
    <bean id="userService" class="com.cjj.service.UserServiceImpl"/>
    <bean id="afterLog" class="com.cjj.log.AfterLog"/>
    <bean id="log" class="com.cjj.log.Log"/>
    <!--方式一:使用原来spring API 接口-->
<!--配置AOP,上面会导入aop命名空间,需要导入aop的约束-->
<!--    <aop:config>-->
<!--        &lt;!&ndash;切入点:express:表达式:execution(要执行的位置!***)&ndash;&gt;-->
<!--        <aop:pointcut id="pointcut" expression="execution(* com.cjj.service.UserServiceImpl.*(..))"/>-->
<!--        &lt;!&ndash;执行环绕通知&ndash;&gt;-->
<!--        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>-->
<!--        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>-->
<!--        &lt;!&ndash;通知:before:前置通知,after:后置通知,after-returning:返回通知,after-throwing:异常通知&ndash;&gt;-->

<!--    </aop:config>-->
    <!--方式二:自定义类-->
   <bean id="diy" class="com.cjj.diy.DiyPointCut"/>
    <aop:config>
        <!--自定义切面,ref:引用自定义切面-->
        <aop:aspect ref="diy">
                <!--切入点,后面的*可以是包下所有的方法,也可以是类下所有的方法,也可以是具体的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.cjj.service.UserServiceImpl.*(..))"/>
        <aop:before method="before" pointcut-ref="pointcut"/>
        <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>

    </aop:config>

</beans>

3、test

import com.cjj.service.UserService;
import com.cjj.service.UserServiceImpl;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applcationContext.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}
//======方法执行前====
//增加了一个用户
//======方法执行后====

方式三:使用注解实现

定义类写注解

import org.aspectj.lang.annotation.Before;

//方式三:使用注解方式实现AOP
@Aspect//标注这个类是一个切面类
public class AnnotationPointCut {
    @Before("execution(* com.cjj.service.UserServiceImpl.*(..))")
    public void before() {

        System.out.println("before");
    }
    @After("execution(* com.cjj.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("after");
    }
    //在环绕增强中,我们可以给定一个目标参数,代表我们要获取处理切入的点
    @Around("execution(* com.cjj.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around before");
        //执行目标方法
        Object proceed = joinPoint.proceed();
        System.out.println("around after");


    }
}

2、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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--注册bean-->
    <bean id="userService" class="com.cjj.service.UserServiceImpl"/>
    <bean id="afterLog" class="com.cjj.log.AfterLog"/>
    <bean id="log" class="com.cjj.log.Log"/>

    <!--方式三-->
    <bean id="annotationPointcut" class="com.cjj.diy.AnnotationPointCut"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>

3、test类

import com.cjj.service.UserService;
import com.cjj.service.UserServiceImpl;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applcationContext.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}

//around before
//before
//增加了一个用户
//after
//around after

回忆mybatis

基于java17
0、maven

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cjj</groupId>
    <artifactId>spring-10-mybatis3</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <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>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <!--Spring操作数据库的话,还需要一个spring-jdbc
               -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

</project>

1、pojo类

package com.cjj.pojo;


import lombok.Data;

@Data
public class User {
        private int id;
        private String name;
        private String pwd;
    }

2、mapper中的usermapper

package com.cjj.mapper;

import com.cjj.pojo.User;

import java.util.List;

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

3、usermapper.xml

<?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.cjj.mapper.UserMapper">

    <!--sql-->
    <select id="selectUser" resultType="user">
        select * from mybatis.user
    </select>
</mapper>

4、mybatis-config.xml

<?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>
<typeAliases>
    <package name="com.cjj.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/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper class="com.cjj.mapper.UserMapper"/>
    </mappers>

</configuration>

5、mytest

import com.cjj.mapper.UserMapper;
import com.cjj.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 sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.selectUser();
        for (User user : userList) {
            System.out.println(user);
        }

    }

}

什么是MyBatis-Spring?

什么是MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>

整合方式一:
1、引入Spring配置文件spring-dao.xml--spring的配置文件

<?xml version="1.0" encoding="GBK"?>
<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>

</beans>

2、配置数据源替换mybaits的数据源

    <!--DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
    我们这里使用Spring提供的JDBC:-->
    <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/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

3、配置SqlSessionFactory,关联MyBatis

    <!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--关联mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/kuang/mapper/*.xml"/>
    </bean>

4\注册sqlSessionTemplate,关联sqlSessionFactory

    <!--SqlSessionTemplate:就是我们使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>

5\需要UserMapper接口的UserMapperImpl 实现类,私有化sqlSessionTemplate

public class UserMapperImpl implements UserMapper {

    //我们的所有操作,都使用sqlSession来执行,在原来,现在都使用SqlsessionTemplate
    private SqlSessionTemplate sqlSession;

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

    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

6\将自己写的实现类,注入到Spring配置文件中。

    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

7\测试使用即可!

    @Test
    public void test () throws IOException {

        ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }

//User(id=2, name=张三, pwd=abcdef)
//User(id=3, name=李四, pwd=987654)
//User(id=4, name=cjj, pwd=123456)
//User(id=5, name=李五, pwd=11111)
现在我们的Mybatis配置文件的状态!发现都可以被Spring整合

其实上面的步骤就是代替

  public void test() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.selectUser();
        for (User user : userList) {
            System.out.println(user);
        }

    }

整合mybatis方式二

1、将我们上面写的UserMapperImpl修改一下

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {

    public List<User> selectUser() {
        
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

2、注入到Spring配置文件中。

    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>

3、测试

    @Test
    public void test () throws IOException {

        ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }

声明式事务

回顾事务
--把一组业务当成一个业务来做;要么都成功,要么都失败!
--事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
--确保完整性和一致性。

事务ACID原则:

原子性(atomicity)
事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性(consistency)
一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中。
隔离性(isolation)
可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
持久性(durability)
事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中。

测试,如果不做事务管理,sql语句写错了还是会被执行

//添加一个用户
int addUser(User user);

//根据id删除用户
int deleteUser(int id);

UserMapper文件,我们故意把 deletes 写错,测试!

<insert id="addUser" parameterType="com.kuang.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

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

编写接口的UserMapperImpl实现类,在实现类中,我们去操作一波

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {


    //增加一些操作
    public List<User> selectUser() {
        User user = new User(5, "小王", "185161");
        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
        mapper.addUser(user);
        mapper.deleteUser(5);

        return mapper.selectUser();
    }
    
    //新增
    public int addUser(User user) {
        return getSqlSession().getMapper(UserMapper.class).addUser(user);
    }

    //删除
    public int deleteUser(int id) {
        return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
    }
}

测试

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

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

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

报错:sql异常,delete写错了

结果 :数据库结果显示插入成功!

没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!

以前我们都需要自己手动管理事务,十分麻烦!

但是Spring给我们提供了事务管理,我们只需要配置即可

Spring中的事务管理

--声明式事务:AOP--一般情况下比编程式事务好用。
--编程式事务:将事务管理代码嵌到业务方法中来控制事务的提交和回滚

  1. 使用Spring管理事务,注意头文件的约束导入 : tx
xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
  1. JDBC事务
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

3、配置好事务管理器后我们需要去配置事务的通知

    <!--结合AOP实现事务的织入-->
    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--给那些方法配置事务-->
        <!--配置事务的传播特性: new -->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

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

propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

  1. 配置AOP,导入aop的头文件
    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.kuang.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
  1. 删掉刚才插入的数据,再次测试!--已经插入失败

为什么需要事务?

如果不配置事务,可能存在数据提交不一致的情况;
如果我们不在Spring中去配置声明式事务,我们就需要在代码中手动配置事务!
事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎!

posted @ 2025-06-15 16:18  乘加法  阅读(6)  评论(0)    收藏  举报