Spring

核心技术 (spring)

  • Spring是一个开源的免费的框架(容器)!

  • Spring是一个轻量级的、非入侵式的框架!

  • 控制反转(IOC),面向切面编程(AOP)!

  • 支持事务的处理。对框架整合的支持!

    Spring就是一个轻量级的**控制反转(IOC)面向切面编程(AOP)**的框架

导入Spring开发的基本包

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



组成

image-20230412144712586


基于Spring的开发:

Spring | Home

image-20230412144812605

  • Spring Boot – 掌握Spring及SpringMVC

    • 一个快速开发的脚手架

    • 基于SpringBoot可以快速的开发单个微服务

    • 约定大于配置


  • Spring Cloud
    • SpringCloud 是基于SpringBoot实现的





常用配置

依赖

<!--Spring基本包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>

<!--AOP织入包-->
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>

配置

<?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:contex="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">

<!-- 指定要扫描的包 ,这个包下的注解就会生效 -->
<contex:component-scan base-package="com.chen.pojo"/>

<!-- 注解驱动:开启注解的支持 -->
<context:annotation-config/>

<!-- 开启AOP注解支持 JDK(默认:proxy-target-class="true") -->
<aop:aspectj-autoproxy/>

</beans>


注解说明

  • Bean自动托管
//@component(类上) 等价于  < bean id="user" class="com.chen.pojo.User" /> 组件,这个类被Spring接管了,注册到了容器中,就是bean!
@component
【等价】
@Repository//dao
@Service//service
@Controller//controller

//@Value(属性上注入) 等价于 : < property name="name" value="辰呀" />
@Value("Value")

  • Bean注解属性注入
// @Autowired 【*】
//(对象属性上) 如果显示定义了Autowired的required,属性为false,说明这个对象可以nuLL,否则不允许为空
@Autowired @Autowired(required = fasle)

//搭配@Qualifier(value="Srping容器中注册的id名称") 实现去容器中匹配该名称的bean
@Autowired
@Qualifier(value="id_Name")

// @Resource
//(对象属性上) 去Spring容器中吗,找唯一,名称相同多个不唯一 --》 找Class类型多个相同不唯一 --》 自定义指定 name
@Resource
@Resource(name="指定id_Name")

  • 作用域
//单例:singleton
//原型:prototype
@Scope("scopeName")

  • AOP
 <!-- 开启AOP注解支持   JDK(默认:proxy-target-class="true") -->
<aop:aspectj-autoproxy/>
    @Aspect //标注这个类是一个切面

//前置通知
@Before("execution(表达式)")

//后置通知
@After("execution(表达式)")

//环绕增强(共同运行)
@Around("execution(表达式)")





IOC

1.推理导论(set注入)

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

1.UserDao接口

2.UserDaolmpl 实现类

3.UserService 业务接口

4.UserServicelmpl 业务实现类


原来需要什么方法则new该对象出现使用其方法

//UserServiceImpl

// 需求不同则需要new 不同的实现类,需要修改原码
private UserDao userDao=new UserDaoImpl();
private UserDao userDao=new UserDaoMysqlImpl();
private UserDao userDao=new UserDaoOracelImpl();

Set方式注入

  • 现在使用set方法注入对象(可理解为封装)

  • 使用一个Set接口实现.已经发生了革命性的变化!

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

//test
//用户实际调用的是业务层,dao层题目不需要接触!
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoImpl());//动态调用实现类
userService.setUserDao(new UserDaoMysqlImpl());//动态调用实现类
userService.getUser();
  • 之前,程序是主动创建对象!控制权在程序猿手上!
  • 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!– 控制反转





2.Spring托管对象

核心技术 (spring.io)

  • 对象是谁创建的?

    对象是由Spring创建的

  • 对象的属性是怎么设置的?

    对象的属性是由Spring容器设置的,

  • 这个过程就叫控制反转


基于 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">

<!--托管:创建的对象(实现类)-->
<bean id="mysqlImpl" class="com.chen.dao.UserDaoMysqlImpl"/>
<bean id="oraclelImpl" class="com.chen.dao.UserDaoOraclelImpl"/>

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

id =变量名
class = new 的对象
-->

<bean id="UserServiceImpl" class="com.chen.service.UserServiceImpl">
<!--
property 相当于对象中的属性设置的一个值
ref 引用Spring容器中(xml)创建好的对象 //要使用这个实现类直接注入
value 具体的值,基本数据类型
-->
<property name="userDao" ref="mysqlImpl"/> <!--相当 new UserDaoMysqlImpl() -->
</bean>

</beans>

  • 使用
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");//配置文件名

//我们的对象现子都在spring中管理了,要使用,去类名取出来使用即可

//自定义强转类型
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");

//固定类型写法
UserServiceImpl userServiceImpl = context.getBean("UserServiceImpl",UserServiceImpl.class);

userServiceImpl.getUser();

对象由Spring来创建,管理,装配



3.IOC构造器注入

1.使用无参构造创建对象,默认!

2.假设我们要使用有参构造创建对象。

​ 1.下标赋值

<!-- 第一种:下标匹配赋值 -->
<bean id="user" class="com.chen.pojo.User">
<constructor-arg index="0" value="辰呀"/>
</bean>

​ 2.类型

<!-- 第二种:通过类型创建,不建议使用 -->
<bean id="user" class="com.chen.pojo.User">
<constructor-arg type="java.lang.String" value="chen"/>
</bean>

​ 3.参数名* name 【*】

<!-- 第三种:直接通过参数名来设置 -->
<bean id="user" class="com.chen.pojo.User">
<constructor-arg name="name" value="辰辰呀"/>
</bean>

总结:在配置文件加载的时候,容器中管理的对象就已经初始化






Spring 配置

别名

<!--别名,如果添加了别名,可以使用别名alias获取对象-->
<alias name="User" alias="userc"/>

Bean的配置

<!-- 
id : bean 的唯一标识符,也就是相当于我们学的对象名
class : bean 对象所对应的全限定名 : 包名 + 类型
name : 也就是别名,而且 name 可以同时取多个别名 /空格、分号、逗号
-->
<bean id="userT" class="com.chen.pojo.UserT" name="user2 userc,userp"/>

import

  • applicationContext.xml
<!-- 可以将多个配置文件导入合并为一个  使用方法不变(id/name)-->

<!-- applicationContext -->
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>

​ 使用时直接使用总的配置即可。





依赖注入

1.构造器注入

<!-- 第三种:直接通过参数名来设置 -->
<bean id="user" class="com.chen.pojo.User">
<constructor-arg name="name" value="辰辰呀"></constructor-arg>
</bean>


2.Set方式注入【重点】

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

【环境搭建】

//类
private String name;
private Adress adress; //引用值(对象 Teacher teacher)
private String[] books; //数组
private List<String> hobbys; //列表
private Map<String,String>card; //集合
private Set<String> games; //集合,该容器中只能存储不重复的对象。
private Properties info; //集合类:每个键和对应的值都是一个字符串(String类型)
private String wife; //
   <!-- 1对象 -->
<bean id="address" class="com.chen.pojo.Adress">
<property name="tid" value="12"/>
<property name="name" value="PC"/>
</bean>

<bean id="student" class="com.chen.pojo.Student">
<!-- 第一种:普通value注入 -->
<property name="name" value="辰呀"/>

<!-- 第二种: Bean注入 ,ref -->
<property name="adress" ref="address"/>

<!-- List数组注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
</array>
</property>


<!-- List列表注入 -->
<property name="hobbys">
<list>
<value>看电影</value>
<value>敲代码</value>
</list>
</property>

<!-- Map集合注入 -->
<property name="card">
<map>
<entry key="身份证" value="1111111111111111"></entry>
<entry key="" value=""/>
</map>
</property>

<!-- Set集合注入 -->
<property name="games">
<set>
<value>LOL</value>
<value>COC</value>
</set>
</property>

<!--String 空>-null-->
<property name="wife">
<property name="wife" value="12"/>
<null></null>
</property>

<!-- Properties集合类注入 -->
<property name="info">
<props>
<prop key="键"></prop>
<prop key="学号">20215120</prop>
<prop key="性别"></prop>
</props>
</property>
</bean>


测试类:

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


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


</beans>

p命名空间注入

<!-- p 命名空间注入,可直接注入属性的值  (相当于property标签) -->  
<bean id="student" class="com.chen.pojo.Student" p:name="辰呀" p:age="18"/>


c命名空间注入

<!-- c 命名空间注入,通过构造器注入:construct-args -->
<bean id="user2" class="com.chen.pojo.User" c:age="22" c:name="辰呀cc"/>


测试

@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans2.xml");
User user = (User) context.getBean("user2");
System.out.println(user);
}

注意点:p命名空间和c空间不能直接使用,需要导入xml约束

xmlns:p="http://www.springframework.org/schema/p"

xmlns:c="http://www.springframework.org/schema/c"



Bean的作用域

  • scope

image-20230412214823755

1.单例模式(Spring 默认机制 多个对象共享一个bean)

<bean id="user2" class="com.chen.pojo.User" c:age="22" c:name="辰呀cc" scope="singleton"/>

2.原型模式(每个bean都对应一个单独对象)每次从容器get的时候,都会产生一个新对象

<bean id="user2" class="com.chen.pojo.User" c:age="22" c:name="辰呀cc" scope="prototype"/>

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






Bean自动装配

  • 即类中属性存在其他对象bean,需要装配注入

  • 自动装配是Spring满足bean依赖一种方式

  • Spring会在上下文自动寻找,并自动给bean装配属性


在Spring中的三种装配的方式

  1. ​ 在xml显显式的装配

    <bean id="user2" class="com.chen.pojo.User" c:age="22" c:name="辰呀cc" scope="prototype"/>

  2. ​ 在java中显式配置

  3. ​ 隐式的自动装配bean【重要**】


1.测试

环境搭建

  • 三个类,A,B,C

  • A中有B,C两个对象,B、C都有一个shout方法

    public class Person {	   
    private B b;
    private C c;
    private String name

    //get\set
    }
  • beans.xml

    <bean id="b" class="com.chen.pojo.B"/>
    <bean id="c" class="com.chen.pojo.C"/>
    <bean id="a" class="com.chen.pojo.A">
    <property name="name" value="辰呀"/>
    <!-- 以下可以通过自动装配实现 -->
    <property name="b" ref="b"/>
    <property name="c" ref="c"/>
    </bean>
  • Test

    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    A a = context.getBean("a",A.class);
    a.b().shout();
    a.c().shout();


2.autowire - Byname

   <bean id="b" class="com.chen.pojo.B"/>
<bean id="c" class="com.chen.pojo.C"/>
<!--
public void setB(B b){
this.b=b;
}
byname:会自动在容器上下文查找,和自己对象set方法后面的值(setB==>b)对应的 bean id
-->
<bean id="a" class="com.chen.pojo.A" autowire="byName/byType">

byName总结:

  • ​ 需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性set方法的值一致


3.autowire – ByType

  <bean id="b" class="com.chen.pojo.B"/>
<bean class="com.chen.pojo.C"/>
<!--
byType:会自动在容器上下文查找,和自己对象属性类型相同的(B) bean id
A中有对象b的类型为B: private B b;
可省略id名,重点class
-->
<bean id="a" class="com.chen.pojo.A" autowire="byName/byType">


byName总结:

  • setB(B b)

  • ​ 需要保证所有bean的id(属性名 b)唯一,并且这个bean需要和自动注入的属性set方法一致B):


byType总结:

  • private B b;

  • 需要保证所有bean的Class(B),并且这个bean需要和自动注入的属性类型一致B):




4.使用注解实现自动装配

将Spring容器中已经注册的bean进行注解装配给目标对象


使用注解:

​ 1.导入约束 – context 约束

xmlns:context="http://www.springframework.org/schema/context"

2.配置注解的支持: context:annotation-config/

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

</beans>

@Autowired

<!--开启注解的支持-->
<context:annotation-config/>
<bean id="cat" class="com.chen.pojo.Cat"/>
<bean id="dog" class="com.chen.pojo.Dog"/>
<bean id="person" class="com.chen.pojo.Person"/>
  • 直接在属性上使用即可,也可以在set方式上使用
  • 使用Autowired 我们可以不用编写Set方法,前提是这个自动装配的实现在IOC(Spring)容器中存在,id且符合名字byname(bytype先后byname都会相辅相成执行)
public class Person {
//自动装配 @Autowired
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;

}

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(Value=”xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入

//xml
<bean id="cat1" class="com.chen.pojo.Cat"/>
<bean id="cat2" class="com.chen.pojo.Cat"/>

//classv
@Autowired
@Qualifier(value="cat2")
private Cat cat;



@Resource

先通过id名称(不唯一)查找、后会通过类型class(唯一)查找,自行选择(name=” “)

@Resource
private Cat cat;

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

总结:

@Autowired和@Resource的区别:

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




使用注解开发

  • 在一个类上使用注解完成bean的配置

  • 在Spring4后,要使用注解开发,必须要保证 aop 的包导入

    导入Spring基本包的时候会导入该包

    image-20230413231730642


  • 使用注解需要导入 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"
xmlns:contex="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">

<!-- 指定要扫描的包 ,这个包下的注解就会生效 -->
<contex:component-scan base-package="com.chen.pojo"/>

<!-- 注解驱动:开启注解的支持 -->
<context:annotation-config/>

</beans>

  • 使注解生效component-scan – 指定要扫描的包 ,这个包下的注解就会生效
<contex:component-scan base-package="com.chen.pojo"/>



@Component

  • 在类中使用 @Component 完成装配

  • 则无需由bean创建对象 – id为类名小写

    @component 等价于 < bean id=”user” class=”com.chen.pojo.User” />

@Component
public class User {
public String name="辰呀";
}

  • Test

    .getBena(“默认为类名的小写”)

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user.name);



@Value

  • 属性注入

  • 定义属性上使用@Value进行赋值:@Value(“辰呀”)

  • 少变量属性使用,多变量使用配置文件注入

  • 在set方法上也可以进行赋值

等价于 : < property name=”name” value=”辰呀” />

public class User {
@Value("辰呀")
public String name;

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



衍生注解

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

  • 【该类被spring托管,实现自动装配】

  • dao【@Repository】

  • service【@Service】

  • controller 【@Controller】

    这四个注解功能都是一样的 ,上三个等价 @Component,都是代表将某个类注册到Spring中,装配Bean




作用域 Scope

  • @Scope(“scopeName”)

  • singleton :.单例模式(Spring 默认机制 多个对象共享一个bean)

  • prototype :原型模式(每个bean都对应一个单独对象)每次从容器get的时候,都会产生一个新对象

@Component
@Scope("singleton")
public class User {
public String name="辰呀";
}

  • 总结 :
  • xml 与 注解

xml更加万能,适用于任何场合!维护简单方便

注解不是自己类使用不了,维护相对复杂

  • xml 与 注解 最佳实践:

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

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



注解说明

  • @Autowired: 自动装配通过类型。名字如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value=”xx”)
  • @Nullable 字段标记了这个注解,说明这个字段可以为null;
  • @Resource: 自动装配通过名字。类型。
  • @Component: 组件,放在类上,说明这个类被Spring管理了,就是bean!
    • @Repository – dao层
    • @Service – service层
    • @Controller – controller层




使用JavaConfig实现配置

  • 想法:我们现现在完全不使用Spring的xml配置了,全权交给Java来做,就是说把xml配置文件转为java程序
  • JavaConfig 是Spring 的一个子项目,在spring 4 之后,成为了核心功能

.@bean可以用于通过方法获取数据库连接池Connect ion这种对象
注意:1.如果开启包扫描,加载配置类以后就可以通过反射拿到配置类中的对象了,
2.@Bean只写在方法上,返回的是一个对象,但一般不获取已经在容器中的对象


  • 实体类
//这里的注解的意思:这个类被Spring接管了,注册到了容器中
@Component
public class User {
private String name;

public String getName() {
return name;
}

@Value("C辰呀")//属性注入值
public void setName(String name) {
this.name = name;
}

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


  • 配置类
//这个也会被Spring容器中托管,注册搭配容器中,这本来就是一个@Component,@Configuration代表这是一个配置类,就和我们之前看的beans.xml
@Configuration

//扫描包下的注解
@ComponentScan("com.chen.pojo")

//把两个类引成一个类,相当于写入多个<bean/>
@Import(ChenConfig2.class)

//配置类 == 配置.xml
public class ChenConfig {

//注册一个bean,就相当于我们之前写的一个bean标签
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值,就相当于bean标签中的class属性
<bean id="getUser" class-"com.chen.pojo.User"/>

@Bean
public User getUser(){
return new User();//返回要注入到bean的对象
}

}


  • 测试类
public class Mytest {
public static void main(String[] args) {
//如果完全使用了配置类方式(没有xml配置文件)去做,我们就只能通过 AnnotationConfig 上下文来获取容器,替换配置类的class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(ChenConfig.class);
User user =context.getBean("getUser",User.class);
System.out.println(user.getName());
}
}

这种纯Java的配置方式,在SpringBoot中随处可见





代理模式

  • AOP的实现机制利用代理实现业务拓展
  • 代码:Spring-08-demo2-demo4

image-20230414152026957


静态代理

角色分析:

  • 抽象角色:一般使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操纵
  • 客户:访问代理对象的人


代理步骤:

1.接口

//租房
public interface Rent {
public void rent();
}

//2
public interface UserService {
public void add();
public void delete();
public void update();
public void query();

}

2.真实角色

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

//2
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("查询了一个用户");
}

}

3.代理角色

//1
public class Proxy implements Rent{
private Host host;

public Proxy() {
}

public Proxy(Host host) {
this.host = host;
}

@Override
public void rent() {
host.rent();
seeHouse;
fare;
hetong;
}

//看房
public void seeHouse(){
System.out.println("中介带你看房");
}

//收中介费
public void fare(){
System.out.println("收中介费");
}

//签租赁合同
public void hetong(){
System.out.println("签租赁合同");
}
}



//2
public class UserServiceProxy implements UserService{

private UserServiceImpl userService;

public void setUserService(UserServiceImpl 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() {
log("query");
userService.query();
}

//日志方法
public void log(String msg){
System.out.println("使用了"+msg+"方法");
}
}

4.客户端访问代理角色

//1
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色。会有附属操作
Proxy proxy = new Proxy(host);

//找代理角色
proxy.rent();
}
}


//2
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();

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


代理模式的好处:

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

缺点:

  • 一个真实角色就会产生一个代理角色; 代码量翻倍 ~ 开发效率会变低



动态代理 – (底层:反射)

  • 动态代理角色和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的!
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口 – JDK 代理 [使用]
    • 基于类:cglib
    • java字节码实现:JAVAssist

需要了解两个类: Proxy 代理 , InvocationHdandler


动态代理的好处:

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

复用静态代理代码2

  • 动态代理类
//使用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

//被代理的接口
private Object target;

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

//生成得到代理类
public Object getProxy(){
// (类的位置、 哪个接口, InvocationHandler:会自己调用invoke方法);
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}

//处理代理实例,返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制
Object result = method.invoke(target, args);//调用被代理类的方法
logcc(method.getName());//代理角色的附加方法,反射获取被代理角色的方法名称
return result;
}

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

}

  • 实现类
public class Client {
public static void main(String[] args) {

//真实角色
UserServiceImpl userService=new UserServiceImpl();

//代理角色,当前不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();

//通过调用程序处理角色来处理我们要调用的接口对象!
pih.setTarget(userService);//设置要代理的对象

//动态生成代理类
UserService proxy = (UserService) pih.getProxy();

proxy.add();//调用真实角色的方法,代理角色附加的方法自动运行
}

}





AOP

image-20230725143859192

AOP 在Spring中的作用

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

image-20230414233445367


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

image-20230414204530934

即Aop 在不改变原有代码的情况下,去增加新的功能


使用Spring实现Aop

  • 代理的是接口

【重点】使用AOP织入,需要导入一个依赖包

<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>


方式一:使用Spring的API接口

  • Log日志类包–实现对应的接口
    • MethodBeforeAdvice:前置通知
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()+"被执行了");

}
}

  • AfterReturningAdvice:带返回的后置通知
public class AfterLog implements AfterReturningAdvice {
//returnValue : 返回值
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}


  • Service
public interface UserService {
public void add();
public void delete();
public void update();
public void selete();
}
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 selete() {
System.out.println("查询了一个用户");
}
}

  • applicationContext.xml配置文件织入AOP
    • 配置AOP: 导入AOP的约束:< aop:config >< /aop:config >
    • 定义切入点

  • 执行环绕增加:< aop:advisor advice-ref=”类” pointcut-ref=”切入点id”/>

    advisor:执行环绕增加!
    advice-ref:引用
    pointcut-ref:引用切入点
    意:log这个类切入到这个方法(execution(方法))上

<?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.chen.service.UserServiceImpl"/>
<bean id="log" class="com.chen.log.Log"/>
<bean id="afterLog" class="com.chen.log.AfterLog"/>

<!-- 方式一:使用原生Spring API接口 -->
<!-- 配置AOP: 导入AOP的约束 -->
<aop:config>
<!--
定义切入点:
pointcut:切入点:定义这是一个切面,切面id、切面的位置expression
expression:表达式,execution(要执行的位置(返回的类型、包名、类名、方法名(参数)) public * * * * *)
例子:目标类UserServiceImpl: *(任意位置) com.chen.service.UserServiceImpl(类) .*(这个类下的任意方法) (..)(任意参数)
-->
<aop:pointcut id="pointcut" expression="execution(* com.chen.service.UserServiceImpl.*(..))"/>

<!--
advisor:执行环绕增加!
advice-ref:引用
pointcut-ref:引用切入点
意:log这个类切入到这个方法(execution(方法))上
-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>

</aop:config>

</beans>

  • Test
public class Mytest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

//动态代理代理的是接口:注意点
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}




方式二:自定义来实现AOP【主要是切面定义】

  • 横切入的类 diyConfig
public class DiyPointCut {
public void before(){
System.out.println("-------方法执行前-------");
}
public void after(){
System.out.println("-------方法执行后-------");
}
}

  • applicationContext.xml配置文件织入AOP
<!-- 方式二:自定义切入类  -->.
<bean id="diy" class="com.chen.diy.DiyPointCut"/>
<aop:config>

<!-- 自定义切面,ref: 要引用的类 -->
<aop:aspect ref="diy">

<!-- 切入点 -->
<aop:pointcut id="point" expression="execution(* com.chen.service.UserServiceImpl.*(..))"/>

<!-- 通知 -->
<!-- 当前ref的bean的方法, 切面id -->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>




方式三:使用注解实现

image-20231017202415177

切面类

  • @Aspect :标注这个类是一个切面
  • 通知

前置通知:**@Before** :@Before(“execution(类的方法及属性)”)

最终通知**@After** :@After(“execution(类的方法及属性)”)

后置通知**@AfterReturning** :@After(“execution(类的方法及属性)”)

异常通知


  • @Around 环绕增强
  • 我们可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(类的方法及属性)")
//使用注解实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {

@Before("execution(* com.chen.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("=========方法执行前=========");
}


@After("execution(* com.chen.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=========方法执行后=========");
}

//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.chen.service.UserServiceImpl.*(..))")
//ProceedingJoinPoint:执行点
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("=========环绕前=========");//还没有正式执行方法

//获取签名 -- 类的信息
Signature signature= joinPoint.getSignature();
System.out.println(signature);

//执行方法
Object proceed = joinPoint.proceed();

System.out.println("proceed:"+proceed);

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

}
}


执行结果:

image-20230725162504324



  • applicationContext.xml配置文件织入AOP
  //切面类  
<bean id="annotationPointCut" class="com.chen.diy.AnnotationPointCut"/>
<!-- 开启注解支持 JDK(默认:proxy-target-class="true") -->
<aop:aspectj-autoproxy/>


Boot中

  • controller所有的方法执行的时候,都会被织入动作
@Aspect
@Component
public class myAdvice {
private Logger logger = LoggerFactory.getLogger(myAdvice.class);

//这个方法实际上并不执行任何操作,它只是用于定义切入点的名称以供后续的通知引用。
@Pointcut(value = "execution( * com.example.spring09aopboot.controller.*.*(..))")
public void myPointcut(){

}
@Around("myPointcut()")
public Object myLogger(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//方法名
String className = proceedingJoinPoint.getTarget().toString();
//参数名
String methodName = proceedingJoinPoint.getSignature().getName();
//方法参数
Object[] args = proceedingJoinPoint.getArgs();
ObjectMapper mapper = new ObjectMapper();
System.out.println("切面:执行前=============类:"+className+"---方法名:"+methodName+"---参数名:"+mapper.writeValueAsString(args));
Object proceed = proceedingJoinPoint.proceed();
System.out.println("切面:执行后=============类:"+className+"---方法名:"+methodName+"---参数名:"+mapper.writeValueAsString(args));
return proceed;
}
}





整合Mybatis

步骤:

1.导入相关jar包

  • junit
  • mybatis
  • mysql数据包
  • spring相关的
  • aop织入
  • mybatis-spring【new】
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>

<!-- Spring操作数据库的话,还需要有关spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>

2.编写配置文件

3测试



Mybatis

1.编写实体类

  • pojo

2.编写核心配置文件

  • mybatis-config.xml

3.编写接口

  • xxxMapper

4.编写Mapper.xml

  • SQL

5.测试

//不使用工具类
//使用Mybatis第一步,获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sessionFactory.openSession(true);


Mybatis-Spring

第一种整合

1、编写数据源配置

  • jdbc

    • 曾经在在 mybatis-config.xml 编写

    •     <!--引入外部配置文件-->
          <properties resource="db.properties">
              <property name="username" value="root"/>
              <property name="password" value="123456"/>
          </properties>
                        
              <environments default="development"><!--环境-->
              <environment id="development">
                  <transactionManager type="JDBC"/><!--事务管理-->
                  <dataSource type="POOLED">
                      <property name="driver" value="${driver}"/>
                      <property name="url" value="${url}"/>
                      <property name="username" value="${username}"/>
                      <property name="password" value="${password}"/>
                  </dataSource>
              </environment>
          </environments>
      



      - 现在 **spring-dao.xml** (spring和mybtais整合配置文件中)编写

      ```xml
      <!-- 固定写法 1~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: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">

      <!--1.关联数据库配置文件-->
      <context:property-placeholder location="classpath:db.properties"/>

      <!--2.连接池
      dbcp:半自动化操作,不能自动连接 (Spring自带)
      c3pB:自动化操作(自动化的加载配置文件,并且可以自动设置到对象中!) (需要导包)
      -->
      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
      <property name="driverClass" value="${jdbc.driver}"/>
      <property name="jdbcUrl" value="${jdbc.url}"/>
      <property name="user" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
      </bean>



2、sqlSessionFactory

曾经在Mybatis中编写

  • public class MybatisUtils {
        private  static  SqlSessionFactory sqlSessionFactory;
        static{
            try{
                //使用Mybatis第一步,获取SqlSessionFactory对象
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession(true);
        }
    }
    

    <br/>

    ```xml-dtd
    <!-- 现在 spring-dao.xml (spring和mybtais整合配置文件中)编写-->

    <!-- 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/chen/mapper/*.xml"/>
    </bean>



3、sqlSessionTemplate

  • 曾经

  • SqlSession sqlSession=sqlSessionFactory.openSession(true);
    



    ```xml
    <!-- 现在 spring-dao.xml (spring和mybtais整合配置文件中)编写-->

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


4、需要给接口加实现类

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


//实现类
public class UserMapperImpl implements UserMapper{

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

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

public List<User> selectUser() {
//曾经Mybatis的操作在这实现
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}


  • mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chen.mapper.UserMapper">

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


</mapper>


5、将自己写的实现类,注入到Spring中

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

<!-- / 1~5 以上为固定写法 -->


6、测试使用即可

    public void selectUser(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

//动态代理代理的是接口:注意点

//动态强制转换为(UserMapper)
//UserMapper userMapper = (UserMapper) context.getBean("userMapper",);

//绑定Spring中注册的bean -- UserMapper
//设置固定类型:UserMapper.class
UserMapper userMapper = context.getBean("userMapper",UserMapper.class);

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



第二种整合

  • 不需要手动注入sqlSession,自动注入

  • 改变实现类,继承 SqlSessionDaoSupport

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {

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

}
}


  • 改变配置文件 spring-dao.xml

  • 不需要构造sqlSession,其他不变,需要构造数据源和sqlSessionFactory

    <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/mybatisb?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>


<!-- 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/chen/mapper/*.xml"/>
</bean>



<!---------------------------------不需要配置--------------------------------->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!---------------------------------------------------------------------------->



<bean id="userMapper2" class="com.chen.mapper.UserMapperImpl2">
<!-- 直接注入sqlSessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

总结

image-20230607151905705

  1. 导依赖(注意maven资源过滤问题)
  2. pojo
  3. 接口
  4. 接口.xml(SQL)
  5. Mybatis配置,①mybatis-config.xml(编写settings和别名typeAliases) ②整合配置spring-dao.xml,最后在Spring中(applicationContext.xml)引用
  6. 接口实现类
  7. 接口实现类注入到Spring中
  8. 通过Spring注册





声明式事务

1、回顾事务

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

事务ACID原则:

  • 原子性

  • 一致性

  • 隔离性

    多个业务可能操作同一个资源,防止数据损坏

  • 持久性

    事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中!



2、spring中的事务管理器

image-20231023162102420

  • 在一个方法中,实现查询,更新,删除,如果删除出现异常,但查询和更新都执行成功了,理论应该回滚
  • 一个错误全部回滚,全对提交事务
  <?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"
xmlns:contex="http://www.springframework.org/schema/context" 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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">





<!-- 配置声明式事务 -->
<bean id="tranasctionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>


<!--结合AOP实现事务的织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="tranasctionManager">
<!--给那些方法配置事务-->
<!--
配置事务的传播特性propagation:new 默认:REQUIRED。REQUIRED:
支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择(增删改)
-->
<tx:attributes>
<!--name匹配方法名开头-->
<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>


<!--配置事务的切入-->
<aop:config>
<!--切入点-->
<aop:pointcut id="txPointCut" expression="execution(* com.chen.mapper.*.*(..))"/><!--该文件所有类和方法都被织入-->
<!--切入事务-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>

事务失效

image-20231023162747904

image-20231023163107568

image-20231023163331137