当前位置:   article > 正文

Spring学习--基于狂神说_artifact [org.springframework:spring-webmvc:jar:5.

artifact [org.springframework:spring-webmvc:jar:5.3.27:compile]

Spring官方中文文档:https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/

1,spring(一个抽象的概念)

1.1 spring的maven地址

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.22</version>
</dependency>
<!--下面这个包是用来和mybatis整合-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.22</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

maven仓库地址:https://mvnrepository.com/

2,IOC思想

​ 让对象可以动态的变化,不写死。

​ 通过一个set方法去给接口赋值

3,Spring项目搭建步骤

第一步:maven项目导入jar包

 <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <!-- spring项目第一步导包 -->
    <!--导入webmvc的包,可以自动的将一些其他spring包导入-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.22</version>
    </dependency>
    <!--下面这个包是用来和mybatis整合-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.22</version>
    </dependency>
  </dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

第二步;创建xml文件(用来实例化对象)(对象==bean)(这是一个存放对象的容器)

<?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
         java中   类型 变量名 = new 类型();
                  Hello hello = new Hello();
         spring中
             id = 变量名
             class = new 的对象
             property相当于给对象中的属性设置一个值!
      -->
    <bean id="userbean" class="pojo.User"/> 
    
    <bean id="hello" class="pojo.HelloSpring">//创建对象并实例化
        <property name="name" value="Spring"/>//给对象属性赋值
        <!--
          如果属性字段是复合型(对象)则不用value,用ref=“另一个对象的bean ID”
      -->
         <!--ref:引用spring容器(这个xml文件)中创建好的对象  value: 基本数据类型的值 -->  
         <property name="user" ref="userbean"/>
    </bean>
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

第三步:测试(运行)

 //获取Spring的上下文对象
 //这句话是固定的,可变的是变量名和参数名,参数名就是需要的(容器)xml文件,可以传多个
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
  //我们的对象现在都在Spring中管理,我们要使用,直接去里面取出来就可以
        Hello hello = (Hello)applicationContext.getBean("hello");
//getBean(String str):这个方法是在beans.xml中取对象,参数为对象的id
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

思考:

  • Hello对象是谁创建的? hello对象是由Spring创建的
  • Hello对象的属性是怎么设置的? hello对象的属性是由Spring容器设置的 【这个过程叫控制反转

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

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

4,ioc(Spring)创建对象的方式:

**1,spring核心:get/set中的set方法,没有set方法无法注入 **

2,正常创建对象是使用的无参构造方法创建对象

<bean id="userbean" class="pojo.User"/>
<!--如果类没有无参构造方法,则无法创建对象和实例化(会报错)-->
  • 1
  • 2

3,使用有参构造方法创建对象的方式:

<!--方式一:下标赋值-->
<bean id="user" class="com.kuang.pojo.User">
        <constructor-arg index="0" value="caishan"/>
<!--index:有参构造方法中,参数的索引位置  value:给这个参数赋的值-->    
</bean>
  • 1
  • 2
  • 3
  • 4
  • 5
<!--方式二:类型赋值  不建议使用(因为如果多个参数类型一致,则赋值会出错) -->
<bean id="user" class="com.kuang.pojo.User">
        <constructor-arg type="java.lang.String" value="caishan"/>
    <!--根据有参构造方法的参数类型去赋值,String类的type是java.lang.String,其他就正常 (类这些除外)-->
</bean>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
 <!--方式三:直接参数名赋值  和property一样的用法,引用对象用ref。只是改了标签名-->
    <bean id="user" class="com.kuang.pojo.User">
        <constructor-arg name="name" value="caishan"/>
    </bean>

  • 1
  • 2
  • 3
  • 4
  • 5

总结:

1,spring创建对象,是在配置文件中bean时就被初始化。

2,如果有有参构造方法,则属性通过property标签进行赋值即可。如果没有有参构造方法(代码中只有有参构造方法)贼需要通过constructor-arg标签对属性进行赋值。

5,spring配置

5.1 alias标签(别名)

 <!--通过别名获取对象,id叫user的bean 可以用abc来代替-->
<alias name="user" alias="abc"/>
   
  • 1
  • 2
  • 3

5.2 Bean标签的配置

 <!--
     id: bean的唯一标识,对象名
     class: bean对象所对应的全限类名: 包名+类名
     name: 也是别名,而且name同时可以取多个别名,中间用空格,逗号分开都可以(比alias更高级)
 -->
 <bean id="user" class="com.kuang.pojo.User" name="ab abc">
        <constructor-arg name="name" value="ppj"/>
    </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5.3 import标签

一般用于团队开发,可以将多个配置文件,导入合并为一个

多人开发,不同的类需要注册到不同的bean中,可以利用import将所有的beans.xml合并

applicationContext.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"/>
    <import resource="beans3.xml"/>
    //导入了三个beans.xml容器
    //使用时直接利用总的xml文件,就可以使用全部的
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

6,DI依赖注入

  • 依赖注入: set注入(重点)
    • 依赖: bean对象的创建依赖于容器
    • 注入: bean对象中的所有属性,由容器来注入

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is 。。。。

不同数据类型的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="adderss" class="pojo.Address">
        <property name="address" value="重庆"/>
    </bean>
    <!---->
    <bean id="Student01" class="pojo.Student">
        <!--普通数据类型,直接使用value注入-->
        <property name="name" value="蔡闪"/>
        <!--其他类数据类型,用ref注入-->
         <property name="address" ref="adderss"/>
        <!--数组类型,在property标签体内使用array标签注入-->
        <property name="books">
            <array>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </array>
        </property>
        <!--list集合类型,在property标签体内使用list标签注入-->
        <property name="hobbys">
            <list>
                <value>111</value>
                <value>222</value>
                <value>333</value>
            </list>
        </property>
        <!--map集合类型,在property标签体内使用map标签注入-->
        <property name="card">
            <map>
                <entry key="ID" value="11223344"/>
            </map>
        </property>
        <!--set集合类型,在property标签体内使用set标签注入-->
        <property name="games">
            <set>
                <value>qqqq</value>
            </set>
        </property>
        <!--null类型,在property标签体内使用null标签注入-->
        <property name="wife">
            <null/>
        </property>
        <!--Properties类型,在property标签体内使用props标签注入(有点像结构体)-->
        <property name="info">
            <props>
                <prop key="姓名">111</prop>
                <prop key="性别"></prop>
            </props>
        </property>
    </bean>
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

几个注意点:

1,如果数据类型是值,则用value 。如果是对象之类的用ref

2,不变的是 这个部分,基本上都可以放在标签体内部,只是普通的用起来方便

3,除了基础的直接在标签内部赋值外,其他的基本上都有自己的标签体。

4,注意map集合,和Properties类型注入的特点

p标签,c标签注入

1,使用前,需要在头文件前加入两句话

 xmlns:p="http://www.springframework.org/schema/p"<!--使用p标签时导入,等价于property -->
 xmlns:c="http://www.springframework.org/schema/c"<!--使用c标签时导入,等价于constructor-arg
-->
  • 1
  • 2
  • 3

2,用法

 <!-- p命名空间注入,直接在标签体内部进行赋值,就相当于格式化-->
<!--p:name="caishan"  等价于    <property name="name" value="caishan "/>  -->
<bean id="user" class="com.kuang.pojo.User" p:name="ppj" p:age="18"/>

 <!-- c命名空间注入-->
<bean id="user2" class="com.kuang.pojo.User" c:name="牛天成" c:age="18"/>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3,Bean的作用域

1,单例模式(Spring默认机制)(scope=“singleton”)(只有一个对象)

<bean id="user2" class="com.kuang.pojo.User" c:name="牛天成" c:age="18" scope="singleton"/>
  • 1

2,原型模式(每次从容器中get的时候,都会产生一个新对象)(scope=“prototype”)

<bean id="user2" class="com.kuang.pojo.User" c:name="牛天成" c:age="18" scope="prototype"/>
  • 1

3,其余的request,session,application这些个只能在web开发中使用到

7,Bean的自动装配

意义:可以不需要手动的给属性赋值(一般用于有引用型属性)

autowire字段选择装配方式

主要用于属性是对象类型的时候

7,1 使用byName自动装配(autowire=“byName”)

<!--
       byName: 会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean的id属性
       例:在该类中有一个People类的属性和setPeople的方法,那么xml中有一个id为people(忽略大小写)的bean,那么这个属性就自动和id为people的bean绑定。
       -->
<bean id="people" class="com.kuang.pojo.People" autowire="byName">
    <property name="name" value="ppj"/>
</bean>
<!--byName的时候,需要保证所有bean的id唯一-->
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

7,2 使用byType自动装配(autowire=“byType”)

<!--   
byType: 会自动在容器上下文中查找, 和自己对象属性类型相同的bean
例:在该类中有一个People类的属性,那么xml中有一个类型为People类的bean,那么这个属性就自动和class为People的bean绑定。
-->
<bean id="people" class="com.kuang.pojo.People" autowire="byType">
    <property name="name" value="ppj"/>
</bean>
<!--byType的时候,需要保证所有bean的class唯一,使用byType可以不用写id-->
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
7,3 使用注解自动装配

使用注解装配,需要先导入context约束,context依赖

<!--在头文件加入-->
    xmlns:context="http://www.springframework.org/schema/context" <!--**-->
        http://www.springframework.org/schema/context <!--**-->
        https://www.springframework.org/schema/context/spring-context.xsd <!--**-->

    <context:annotation-config/>//导入自动装配的依赖
</beans>
提前也要在spring中注册
除非自动装配的类也用注解进行了注册
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注解加入的位置:

//可以加在属性的头上(注意:每一个属性都自己加自己的)
  @Autowired(required = false)//如果定义@Autowired(required = false),说明这个对象可以为null,否则不允许为空(默认是true)
    private Cat cat;
  @Autowired//也可以不加后面的约束
    private Cat cat;

//也可以直接加在set方法头上
//也可以忽略set方法,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byName默认是通过byType (就是说如果用注解自动装配可以不写set方法但是要在spring中配置,否则不用配置)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果有多个同类的对象,则无法进行自动的绑定需要使用**@Qualifier(value = “绑定对象的ID名字”)**注解进行配套使用

<!--这是另一个配置注解,可以从byName,byType两种方式去匹配--> 
@Resource
    private Cat cat;

@Nullable  属性标记了这个注解,说明这个字段可以为null
  • 1
  • 2
  • 3
  • 4
  • 5

小结:

@Resource和@Autowired的区别:

都是用来自动装配的,都可以放在属性字段上
@Autowired通过byType的方式实现,而且必须要求这个对象存在 【常用】
@Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到,就会报错
不同:@Autowired通过byType的方式实现 @Resource默认通过byName的方式实现

8,注解开发

使用注解开发的第一步还是需要先导入context约束,依赖

<!--在头文件加入-->
 <?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="包的全路径"/><扫描包>
   // <context:annotation-config/>//导入的依赖
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2,需要在指定包的每一个类上加上这个注解**@Component** :意思是该包的对象就在spring容器中被创建了。

该对象的名字为类名的首字母小写形式,也可以在里面写上想要的名字

@Component("注册名")  //等价于 <bean id="user" class="pojo.User">
public class User {
     <!--需要给对象的属性赋值,则使用@Value("值")注解-->
         
    //等价于 <property name="name" value="ppj"/>
    @Value("ppj")
    private String name;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3, 衍生的注解
@Componet有几个衍生注解,我们在web开发中,会按照mvc三层架构分层

dao [@Repository]

service [@Service]

controller 【@Controller】

这4个注解的功能都是一样的(都等价于@Componet),都是代表将某个类注册到Spring中,装配Bean

4,自动装配注解

@Resource

@Autowired

5,作用域注解

@Scope(“prototype”) [代表该类是单例模式]

6,总结

xml与注解

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

最佳实践:

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

9,使用java的方式配置spring

其中一个方法就是一个bean对象

//@Configuration代表这是一个配置类,就和我们之前看的beans.xml是一样的
@Configuration
@ComponentScan("包名")//扫描包。等价于 <context:component-scan base-package="包的全路径"/>
@Import(配置类名.class)//类似将多个xml文件导成一个
public class MyConfig {

    //注册一个bean,就相当于我们之前写的一个bean标签
    //这个方法的名字,就相当于bean标签中的id属性(对象名)
    //这个方法的返回值,就相当于bean标签中的class属性(类名)
    @Bean   //等价于<bean id="getUser" class="com.XXX.User"/>
    public User getUser(){
        return new User();    //就是返回要注入到bean的对象
    }
}
//第二个配置类
@Configuration
public class MyConfig2 {
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

调用时:获取池子的方法有所改变

如果完全使用了配置类方式去做,我们就只能通过AnnotationConfigApplicationContext来获取容器,括号里面写这个配置类的类名.class

public class MyTest {

    @Test
    public void test(){
        //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfigApplicationContext来获取容器,通过配置类的class对象加载
 ApplicationContext context = new AnnotationConfigApplicationContext(类名.class);
        User user = context.getBean("getUser", User.class);
        System.out.println(user);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这种纯java的配置方式,在springboot中随处可见!

10,代理模式

注意:一个代理只能对应一个接口

10,1静态代理模式:

代理模式的意义是:为了不改动原有代码【满足开放封闭-原则】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xQ18q3qK-1668096073684)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729125335472.png)]

角色分析:

  • 抽象角色: 一般会使用接口或者抽象类类解决(房子)(接口类)
  • 真实角色:被代理的角色(房东)(实现类)
  • 代理角色: 代理真实角色,代理真实角色后,我们一般会做一些附属操作(代理)(调实现类的方法)
  • 客户:访问代理对象的人(租客

代码步骤:

​ 抽象角色:

//房子
public interface Room {
    //租房
    public void  Rent();
}
  • 1
  • 2
  • 3
  • 4
  • 5

​ 真实角色:

//房东
public class Host implements Room {
    @Override
    public void Rent() {
        System.out.println("我要出租房子");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

​ 代理角色:(继承接口)

//代理
public class Proxy {
    private Host host;
    public Proxy() {
    }
    //客户找代理时,指定需要哪一个房东的房子
    public Proxy(Host host) {
        this.host = host;
    }
    public void Rent() {
        //租房
          host.Rent();
           Money();
    }
    //代理负责收租
    private void Money(){
        System.out.println("代理收租");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

​ 租客:

//租客
public class Me {
  public static void main(String[] args) {
    Host host=new Host();
    //找到代理,告诉他我要租谁的房子
    Proxy proxy=new Proxy(host);
    //进行租房
    proxy.Rent();
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

代理模式的好处:

  1. 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  2. 公共业务也就交给代理角色,实现业务的分工
  3. 公共业务发生扩展的时候,方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色;代码量会翻倍===开发效率变低(静态代理只允许,一个房东一个代理)
10,2案例二

业务接口:(接口)

public interface UserService {

    /*
       需求: 在不修改原代码的情况下,添加日志打印信息
     */

    public void add();
    public void delete();
    public void update();
    public void query();

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

真实业务:(实现类)

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("add方法");
    }

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

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

    @Override
    public void query() {
        System.out.println("query方法");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

需求:在不改变原有代码的基础上,实现日志打印,借助代理

public class UserServiceProxy implements UserService{
    @Override
    public void add() {
        log("add");
        System.out.println("add===");
    }
    @Override
    public void delete() {
        log("delete");
        System.out.println("delete===");
    }
    @Override
    public void update() {
        log("update");
        System.out.println("update=====");
    }
    @Override
    public void query() {
        log("query");
        System.out.println("query=====");
    }
    public void log(String msg){
        System.out.println("[DEBUG]"+msg+"方法执行");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

测试:

public static void main(String[] args) {
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        userServiceProxy.add();
    }
  • 1
  • 2
  • 3
  • 4

直接用代理对象实现同样的业务接口,打印日志信息,而不触及到原代码

10,3动态代理

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

InvocationHandler

Proxy

动态代理的好处:

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

直接动态代理常用工具类(基于接口:JDK动态代理)

可以直接封装,使用

public class ProxyInvocationHandler implements InvocationHandler{
    private Object target;//需要代理的接口(object可以是具体的接口名,target可以是任意名字)
    public void SetTarget(Object target){
        this.target=target;
    }
    //得到生成的代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass.getInterfaces,this);
    }
    //处理代理实例,返回结果(处理客户端选择执行的方法)
    //Method:方法   Object[]:应该是参数  Proxy:代理类
    public Object invoke(Object Proxy,Method method,Object[] args)throws Throwable{
        //其他代理类额外添加的方法,写在这里面
        Object result =method.invoke(target,args);//执行选择的方法
        return result;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

调用:

public Static void main(String[] args){
//创建真实角色(这一步没变)
UserServiceImpl userService=new UserServiceImpl();
//创建代理角色
ProxyInvocationHandLer pih =new ProxyInvocationHandLer();//调用之前的自动生成代理类方法
pih.setTarget(userService);//将需要代理的真实角色传递给他
UserService proxy=(UserService)pih.getProxy();//创建代理类(UserService:接口名)
proxy.query();//利用该代理类调用方法
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

11,AOP(动态代理)(核心还是反射)

提供声明式事务;允许用户自定义切面

横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …(用AOP插入的情况

切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类

通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

目标(Target):被通知对象。

代理(Proxy):向目标对象应用通知之后创建的对象。

切入点(PointCut):执行方法的位置

连接点(JointPoint):与切入点匹配的执行点。

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aiSxyMag-1668096073686)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729161213739.png)]
即AOP在不改变原有代码的情况下,去增加新的功能

使用AOP置入,需要导入一个依赖包[重点]

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.8</version>
    <scope>runtime</scope>
</dependency>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
实现AOP的第一种方式:(API接口)

1,编写业务接口和实现类

public interface UserService {

    public void add();
    public void delete();
    public void update();
    public void select();
}



public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("add方法。。。");
    }
    @Override
    public void delete() {
        System.out.println("delete方法。。。");
    }
    @Override
    public void update() {
        System.out.println("update方法。。。");
    }
    @Override
    public void select() {
        System.out.println("select方法。。。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

2,编写增强类,后置和前置增强(Advisor)[继承不同的接口]

//MethodBeforeAdvice:前置增强
public class BeforeLog implements MethodBeforeAdvice {

    //method: 要执行目标对象的方法
    //args: 被调用方法的参数
    //target: 目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"执行了"+method.getName()+"方法");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
//AfterReturningAdvice:后置增强
public class AfterLog implements AfterReturningAdvice {
    //returnValue 返回值
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
         System.out.println(target.getClass().getName()+"执行了"+method.getName()+"方法"+returnValue);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3,将这些类注册到Spring容器中,并实现aop切入,导入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
        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 id="userService" class="com.kuang.service.UserServiceImpl"/>
      <bean id="beforeLog" class="com.kuang.log.BeforeLog"/>
      <bean id="afterLog" class="com.kuang.log.AfterLog"/>
    
      <!-- AOP配置   -->
      <aop:config>
          <!-- 配置切入点  expression:是一个表达式-->      
         <aop:pointcut id="pointcut"  //这个名字可以随便取                                      expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>//固定写法
          execution(* 哪一个类.*全部方法(..)//代表多个参数)
          <!--  执行环绕 advice-ref执行方法 . pointcut-ref切入点-->
          <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
          <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
      </aop:config>
    //aop:config  标签的完整意思:在UserServiceImpl这个实现类的所有方法中配置切入点,然后在执行这些方法时执行advice-ref定义的方法
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

4.测试

public class MyTest {
    public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理的是接口,虽然绑定的对象是一个接口的实现类,但是返回值还是一个接口
        UserService userService = (UserService) context.getBean("userService");
        userService.delete();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
实现AOP的第二种方式:(自定义类)

1,将之前的增强类变为一个普通类,里面写多个方法

public class MyAspect {
  //方法就是之前的各种增强类,名字随便起
    public void before(){
        System.out.println("before通知=====");
    }

    public void after(){
        System.out.println("after通知=======");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2,在spring中配置

   <!-- 方式二: 自定义切面类   -->
      <bean id="myAspect" class="com.kuang.def.MyAspect"/>

    <aop:config>
         <!--aspect:自定义切面  ref要引用的类  -->//自定义方法必须用aop:aspect这个标签
        <aop:aspect ref="myAspect">
             <!--  切入点 -->
<aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
             <!--   通知(要切入的方法)  -->
            <aop:before method="before" pointcut-ref="pointcut"/>
  //我要切入ref这个类中的before()方法,在pointcut指定的切入点切入,什么时候切入?aop:before的时候
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3,测试

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.delete();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
实现AOP的第三种方式:(注解实现)

不需要导入注解的约束【因为不是用注解写对象】

只需要导入aop相关约束即可

在xml中需要新增一个标签

开启注解支持
<aop:aspectj-autoproxy/>
  • 1
  • 2
@Aspect   //标注这个类是一个切面
public class AnnotationAspect {
  //前置通知
@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
    System.out.println("执行方法前====");
}
  //后置通知
@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
    System.out.println("执行方法后=====");
}
  //环绕通知
@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
    System.out.println("环绕前=====");
    jp.proceed();  //执行方法
    System.out.println("环绕后=====");
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

12,整合mybatis

mybatis是写接口,写mapper.xml文件,写SQL

Spring就是万物皆对象,每一个类都是一个对象,在beans.xml中实例化之后才可以使用

步骤:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y0VBwCly-1668096073686)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729180248262.png)]

  1. 导入相关jar包

    • junit

    • mybatis

    • mysql

    • spring

    • AOP

    • spring-jdbc【新的】

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8PNVdPOm-1668096073687)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729180039423.png)]

    • mybatis-spring【新的】

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4u8MewxF-1668096073687)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729180200901.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KyK3Ymev-1668096073687)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729175925321.png)]

1,编写配置文件

2,测试

12.1 回顾mybatis
  1. 编写实体类
  2. 编写核心配置文件
  3. 编写接口
  4. 编写Mapper.xml
  5. 测试
12.2 整合mybatis方式一

利用SqlSessionTemplate。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJKEh0Il-1668096073688)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220730135529798.png)]

第一步:编写数据源

在spring的配置文件中编写以下代码

    <!-- DataSource:使用Spring的数据源替换mybatis的配置  c3p0 dbcp druid
         这里使用spring提供的jdbc: org.springframework.jdbc.datasource
       -->
    <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?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

第二步:创建SqlSessionFatory对象

<!-- 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"/>//意思是:相当于绑定注册,绑定com/kuang/mapper/*.xml这个路径下的所有Mapper.xml
    </bean>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

第三步:创建sqlsession对象(在spring中叫sqlSessionTemplate)

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--  只能用有参构造器注入sqlSessionFactory  因为它没有set方法      -->
    <constructor-arg ref="sqlSessionFactory"/>
</bean>
//第二,第三步相当于之前写的mybatis工具类
  • 1
  • 2
  • 3
  • 4
  • 5

如何使用?

1,需要给接口加实现类

在原来mybatis的基础上给mapper接口添加实现类(实现类去实现具体的调用步骤)

public class UserMapperImpl implements UserMapper {

    private SqlSessionTemplate sqlSession;//在实现类中定义一个SqlSessionTemplate对象

    public void setSqlSession(SqlSessionTemplate sqlSession) {//需要写一个set方法,一会方便在beans.xml中进行注入
        this.sqlSession = sqlSession;
    }
    @Override//这就是类似指定调用的SQL语句
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);//还是需要映射一个接口的对象调用方法执行SQL
        List<User> userList = mapper.selectUser();//执行SQL
        return userList;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2,将自己写的实现类,注入到spring中(创建一个实现类的对象)

<!-- 总的spring配置文件 -->
<bean id="UserMapperImpl" class="com.kuang.mapper.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>//将SQLsession对象注入
</bean>
  • 1
  • 2
  • 3
  • 4

3,测试

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

遇见的错误

mybatis-config.xml里的mappers 要删了,     
这个和 spring-dao的
<property name="mapperLocations"value="classpath:com/lzh/mapper/*.xml"/>
只能二选一!!!!!!不然会报错的,都是mapper的作用!
  • 1
  • 2
  • 3
  • 4
12.3 整合mybatis方式二

利用继承SqlSessionDaoSupport

两个点:不用再配置sqlsession对象,实现类继承

在实现类中写:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8cxGvJ2s-1668096073688)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220730162524102.png)]

在beans.xml中写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4nTgsIUz-1668096073688)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220730162708466.png)]

相当于省略了beans.xml中配置sqlsession的步骤

13,事务

spring中的事务管理分为

  • 声明式事务:AOP(不影响原有代码)
  • 编程式事务:需要在代码中进行配置管理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XjhFgLkD-1668096073689)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220730170603636.png)]

步骤:

1,配置声明式事务(开启事务管理器)

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"   ref="dataSource"/>
</bean>
<!--这是死代码 ,dataSource:数据源-->
  • 1
  • 2
  • 3
  • 4

2,结合AOP实现事务的织入

  <!--1, 导入事务的约束  (头文件处)-->
//注意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:tx="http://www.springframework.org/schema/tx"//导入的
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx//导入的
        https://www.springframework.org/schema/tx/spring-tx.xsd //导入的    
                           ">

<!--2, 配置事务通知(方法)   -->
     <!--  transactionManager步骤一所配置的bean  -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 给哪些方法配置事务   -->
        <!-- 配置事务的传播特性: propagation=“required”  spring默认事务 -->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>//所有add方法配置事务
            意思是:给所有的add方法配置REQUIRED类型的事务
            <tx:method name="delete" propagation="REQUIRED"/>//所有delete方法配置事务
            <tx:method name="query" read-only="true"/>//read-only表示query类的事务无法对数据库进行增删改
            <tx:method name="*"/>//所有方法配置事务
        </tx:attributes>
    </tx:advice>

    <!--3,配置事务切入(切入点)   -->
    <aop:config>
        <aop:pointcut id="txPoint" expression="execution(* com.kuang.mapper.*.*(..))"/>切入点
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>//切入的方法
    </aop:config>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

AOP:切面,通知,切入点

为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致的情况下
  • 如果我们不在spring中去配置声明式事务,我们就需要在代码中手动配置事务
  • 事务在项目开发中重要,设计到数据的一致性和完整性问题,不容马虎
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/86810
推荐阅读
相关标签
  

闽ICP备14008679号