Spring
Spring
优点
- 是一个开源的免费框架(容器)
- 是一个轻量级的、非入侵式的框架
- 控制反转,面向切面编程
- 支持事务的处理,对框架整合的支持
组成
扩展
- Spring Boot
- 一个快速开发的脚手架
- 基于Spring Boot 可以快速开发单个微服务
- 约定大于配置
- Spring Cloud
- 基于 Spring Boot 实现的
弊端:发展了太久之后,违背了原来的理念,配置十分繁琐
IOC
理论推导
- UserDao接口
- UserDaoImpl实现类
- UserService业务接口
- UserServiceImpl 业务实现类
- 通过不同类型的注入,可以不再管理对象的创建,而是通过用户选择
这种思想,从本质上解决了问题,不需要再去管理对象的创建了,系统的耦合性大大降低
本质
HelloSpring
demo2
使用Spring创建对象,在Spring中这些都称为Bean
使用xml创建对象并且赋值,而不是通过new来创建对象,这种方式创建的对象就是bean
本质是使用set进行对象的注入
<property name="str" value="Spring"/>
<property name="userDao" ref="mysqlImpl"/>
- value:基本的值,基本数据类型
- ref:引用Spring容器中创建好的对象
需要对象直接从Bean里面get就行了,不需要再创建对象了,这就是IOC
同样的,需要修改和引入直接简单修改配置文件就好了
Spring现在就相当于一个生产对象的大工厂,需要什么直接从工厂提货就行,不需要自己创造
IOC 创建对象的方式
对应的POJO类
package com.xuuxxi.pojo;
/**
* @Author: Xuuxxi
* @Date: 2022/4/8
*/
public class People {
private Long id;
private String name;
private String password;
public People(Long id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public People() {
}
@Override
public String toString() {
return "People{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
赋值的三种方式 && 标签的意义
<!--第一种,下标赋值-->
<!--需要对应的构造函数-->
<bean id="t1" class="com.xuuxxi.pojo.People">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="tom"/>
<constructor-arg index="2" value="123"/>
</bean>
<!--第二种,类型赋值-->
<!--不建议使用-->
<bean id="t2" class="com.xuuxxi.pojo.People">
<constructor-arg type="java.lang.Long" value="2"/>
<constructor-arg type="java.lang.String" value="jack"/>
<constructor-arg type="java.lang.String" value="123"/>
</bean>
<!--第三种,通过name赋值-->
<bean id="t3" class="com.xuuxxi.pojo.People">
<constructor-arg name="id" value="3"/>
<constructor-arg name="name" value="Can"/>
<constructor-arg name="password" value="123"/>
</bean>
总结
- 在配置文件加载的时候,容器中的对象就已经初始化了
Spring配置
Bean的配置
<!--
id:bean的唯一标识符,也就是相当于对象名
class:bean对象所对应包路径
name:别名,可以同时取多个别名(可以通过逗号、空格、分号分隔)
> x1,xx1,xxx1都是t3的别名
-->
<bean id="t3" class="com.xuuxxi.pojo.People" name="x1,xx1,xxx1">
alias
<!--
另一种别名的方法
给t1取别名为tom
-->
<alias name="t1" alias="tom"/>
import
- 一般用于团队开发使用,可以将多个配置文件导入合并成一个
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
<!--
有多个配置文件需要合并
最后只需要导入合并过后的applicationContext.xml文件就可以
-->
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
依赖注入
构造器注入
- 之前的Bean就是构造器注入
Set方式注入
重点
position : demo3
<bean id="student" class="com.xuuxxi.pojo.Student">
<!--普通注入-->
<property name="name" value="zhang"/>
<!--Bean注入-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>a</value>
<value>b</value>
<value>c</value>
<value>d</value>
</array>
</property>
<!--list注入-->
<property name="hobbies">
<list>
<value>a</value>
<value>b</value>
<value>c</value>
<value>d</value>
</list>
</property>
<!--map-->
<property name="cards">
<map>
<entry key="a" value="1"/>
<entry key="b" value="2"/>
<entry key="c" value="3"/>
<entry key="d" value="4"/>
</map>
</property>
<!--set-->
<property name="games">
<set>
<value>a</value>
<value>b</value>
<value>c</value>
<value>d</value>
</set>
</property>
<!--null && empty-->
<!--null-->
<property name="wife">
<null/>
</property>
<!--empty-->
<!--<property name="wife" value=""/>-->
<property name="info">
<props>
<prop key="a">1</prop>
<prop key="b">2</prop>
<prop key="c">3</prop>
<prop key="d">4</prop>
</props>
</property>
</bean>
<bean id="address" class="com.xuuxxi.pojo.Address"/>
C空间和P空间命名注入
- 两种拓展方式
p空间
-
依赖于set
xmlns:p="http://www.springframework.org/schema/p"
<!--需要在文件头部声明引用p空间注入-->
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.xuuxxi.pojo.User" p:name="zhang" p:age="18"/>
</beans>
c空间
-
依赖于有参构造器
xmlns:c="http://www.springframework.org/schema/c"
<!--需要在文件头部声明引用c空间注入-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.xuuxxi.pojo.User" c:name="zhang" c:age="18"/>
</beans>
Bean的作用域
作用域 | 描述 |
---|---|
单例(singleton) | (默认)每一个Spring IoC容器都拥有唯一的一个实例对象 |
原型(prototype) | 一个Bean定义,任意多个对象 |
请求(request) | 一个HTTP请求会产生一个Bean对象,也就是说,每一个HTTP请求都有自己的Bean实例。只在基于web的Spring ApplicationContext 中可用 |
会话(session) | 限定一个Bean的作用域为HTTPsession 的生命周期。同样,只有基于web的Spring ApplicationContext 才能使用 |
全局会话(global session) | 限定一个Bean的作用域为全局HTTPSession 的生命周期。通常用于门户网站场景,同样,只有基于web的Spring ApplicationContext 可用 |
应用(application) | 限定一个Bean的作用域为ServletContext 的生命周期。同样,只有基于web的Spring ApplicationContext 可用 |
- singleton
- 永远只有一个对象被创建,不管get多少次
- prototype
- 一次get就创建一个新的对象
- 其余的作用域只能在web开发中使用到
Bean的自动装配
- 自动装配是Spring满足Bean依赖的一种方式
- Spring会在上下文中自动寻找,并自动装配属性
有三种装配的方式
- 在xml中显示的配置
- 在java中显示配置
- 隐式的自动装配 Bean
byName && byType 自动装配
<bean id="cat" class="com.xuuxxi.pojo.Cat"/>
<bean id="dog" class="com.xuuxxi.pojo.Dog"/>
<!--
autowire="byName"
会自动在上下文中查找和自己对象 set 方法后面的值对应的 bean
需要保证 bean 的 id 唯一性
autowire="byType"
会在上下文中查找符合自己对象属性类型相同的 bean
需要保证 bean 的 class 唯一性
-->
<bean id="people" class="com.xuuxxi.pojo.People" autowire="byName">
<property name="name" value="a"/>
</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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
@Autowired && @Qualifier
- 开发中最常用
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
- 自动装配,代替ref
- 如果定义
@Autowired(required = false)
则注入对象可以为空 - 若自动黄培环境比较复杂的时候,可以使用
@Qualifier(value="xxx")
去配置@Autowired
的使用,指定一个唯一的bean进行注入
@Resource
- 更高级,java的原生注解,功能更强大
- 多个同类型找名字,单个类型直接注入不识别名字(byName && byType)
- 可以用
(name = xxx)
来指定注入对象
小结
@Resource 和 @Autowired 的区别
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired 通过 byType 方式实现
- @Resource 默认是 byName 实现,实现失败则尝试进行 byType 实现
- 开发中常用 Autowired
- Resource 可以看成 Autowired 和 Qualifier 的集合体
使用注解开发
- 需要导入 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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!--指定要扫描的包,指定包下的注解生效-->
<context:component-scan base-package="com.xuuxxi.pojo"/>
</beans>
@Component
- 等价于
<bean id="user" class="com.xuuxxi.pojo.User"/>
- 一定要注意配置注解生效位置,否则无法生效实现自动注入
衍生注解(功能相同)
不同层下的不同注解方式,但是功能都是和 @Component 等价的
- dao
@Repository
- controller
@Controller
- service
@Service
@Value
- 在属性上加,给属性注入值
- 等价于
<property name="name" value="test"/>
@Component
public class User {
@Value("test")
private String name;
}
小结
xml 与 注解
- xml 更加万能,适用于任何场合,维护简单方便
- 注解 不是自己的类是用不了,维护相对复杂
最佳实践方式
- xml用来管理bean
- 注解值负责完成属性的注入
- 使用的过程中只需要注意一个问题:想让注解生效就必须开启xml对注解的支持
JavaConfig方式配置
可以完全不适用Spring的xml配置,完全交给Java来做
@Configuration
- 使用方法
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
前面的配置与下面的 Spring XML 完全等效
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
- 测试
- 编写pojo
//类被注册到容器中
@Component
public class User
- 编写Config文件
package com.xuuxxi.config;
import com.xuuxxi.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
//声明为配置类,相当于之前的beans.xml
@Configuration
//@ComponentScan 默认开启,也可以自己指定注解生效的包
@Import(MyConfig2.class) //相当于引入第二个xml文件
public class MyConfig {
@Bean //注入Bean,Bean的id就是方法名(id="getUser")
public User getUser(){
return new User();
}
}
- 获取context方法
import com.xuuxxi.config.MyConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
public class MyTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Object user = context.getBean("getUser");
System.out.println(user);
}
}
这种纯Java的配置方式,在SpringBoot中非常常见
代理模式
demo8
SpringAOP的底层!!
优点:
- 可以使真实角色的操作更加纯粹!不用再关注一些公共的业务
- 公共业务交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量变多,开发效率变低
静态代理
角色分析:
- 抽象角色:一般会使用接口或抽象类来完成
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤:
- 接口
package com.xuuxxi.demo1;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
public interface Rent {
public void rent();
}
package com.xuuxxi.demo2;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
- 真实角色
package com.xuuxxi.demo1;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
//房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("Host rent...");
}
}
package com.xuuxxi.demo2;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
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");
}
}
- 代理角色
package com.xuuxxi.demo1;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
//中介
public class Proxy implements Rent{
private Host host;
public Proxy(){}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
host.rent();
Sign();
fee();
}
public void seeHouse(){
System.out.println("See...");
}
public void fee(){
System.out.println("Fee...");
}
public void Sign(){
System.out.println("Sign...");
}
}
package com.xuuxxi.demo2;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
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 + " 方法");
}
}
- 客户端访问代理角色
package com.xuuxxi.demo1;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
public class Client {
public static void main(String[] args) {
//房东租房
Host host = new Host();
//中介做附加操作
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
package com.xuuxxi.demo2;
/**
* @Author: Xuuxxi
* @Date: 2022/4/9
*/
public class Client {
public static void main(String[] args) {
UserServiceImpl service = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(service);
proxy.add();
}
}
这样做的好处就是改变公共操作不用修改原有的业务代码,这是大忌
加深理解
- 关键就是横向开发,不改变原来的业务代码
动态代理
-
动态代理和静态代理角色一样
-
动态代理的代理类是动态生成的,不是写好了调用的
-
动态代理分为两大类
-
基于接口 --- JDK 动态代理
-
基于类 --- cglib
-
java 字节码实现 :javasist (仅了解)
代码公式
- target就是要实现的功能接口
package com.xuuxxi.demo4;
import com.xuuxxi.demo3.Rent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Author: Xuuxxi
* @Date: 2022/4/11
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target){
this.target = target;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//处理代理实例并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//其余功能
log(method.getName());
//反射机制
//功能实现
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println(msg + " 方法");
}
}
- Client 使用
package com.xuuxxi.demo4;
import com.xuuxxi.demo4.ProxyInvocationHandler;
/**
* @Author: Xuuxxi
* @Date: 2022/4/11
*/
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
什么是 AOP
使用Spring实现AOP
- 需要导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
- application.xml 中引入约束
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
方式一:API 接口
demo9
通过切入点和环绕实现代理前后的操作
方式二:使用自定义类
demo9
通过自定义切面实现
方式三:使用注解
demo9
自定义类,使用注解标记切入点
推荐使用方法二
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.xuuxxi"/>
<!--方式一:使用原生API接口-->
<aop:config>
<!--切入点-->
<!--
expression : 表达式,是固定的
execution : 要执行的位置,第一个 * 表示返回所有类型,后面是路径,(..) 是方法的参数类型
-->
<aop:pointcut id="pointcut" expression="execution(* com.xuuxxi.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
<!--方法二:使用自定义类-->
<aop:config>
<!--自定义切面,ref 就是要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.xuuxxi.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
<!--方式三:使用注解 JDK(DEFAULT) while proxy-target-class="false" cglib while proxy-target-class="true"-->
<aop:aspectj-autoproxy/>
</beans>
声明式事务
事务
- 把一组业务当成一个业务来做,要么都成功,要么都失败
- 事务再项目开发中,十分的重要,涉及到数据的一致性问题
- 确保完整性和一致性
事务ACID原则
- 原子性
- 一致性
- 隔离性
- 持久性
Mybatis && 事务问题
暂时不学