Spring
-
简介
- 2002年spring框架的雏形:interface21
- 2004年3月24日发布Spring1.0
- 作者:Rod Johnson。他是一个音乐博士
- SSH(Struct2+Spring+Hibernate)
- SSM(SpringMVC+Spring+Mybatis)
- 官网:https://spring.io
- 官方下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring
- GitHub:https://github.com/spring-projects/spring-framework
-
优点
- 免费框架(容器)
- 轻量级、非入侵
- 控制翻转IOC、面向切面棉城AOP
- 支持事务、对框架整合的支持
-
扩展
- SpringBoot
- 一个快速开发的脚手架
- 快速开发单个微服务
- 约定大于配置
- SpringCloud
- 基于SpringBoot
- SpringBoot
IOC理论推导
- 创建工程(基于idea+Maven)
- 建议每次创建新的工程后,在idea中检查配置一下Tomcat、Maven都修改成自己安装的,而不是用idea自带的,方便以后维护。
- 建议每个模块都分别建各自的Module,而不要直接在工程下开发。
- 新建Maven模块后,idea会自动加载依赖包,可以在idea右下角“后台任务”区域查看,或者idea右侧toolbar->Maven选项卡中查看,务必等待加载完成。
- 在pom.xml中添加依赖:Spring Web MVC(…),同样需要等全部加载完成。
- 传统结构
- UserDao
- UserDaoImpl
- UserService
- UserServiceImpl
IOC原理
- 控制反转。即:添加set注入,程序不再主动创建,而是被动接受对象。
IOC创建对象的方式
- BeanFactory:仅加载配置文件,只有在使用里边的bean时菜创建对象,一般不用于开发人员使用
- ApplicationContext:在配置文件被加载的时候,容器中管理的对象就已经被初始化了,一般用于开发人员使用。
Spring配置
- 别名:,也可以直接在bean标签中添加name属性且更高级。
- 配置
- 导入:,一般用于团队开发使用,一人一个beans.xml文件最后合并为一个。
依赖注入(DI)
-
构造器注入
<!-- 下标创建方式,必须定义有参构造器 --> <bean id="user" class="com.baidu.pojo.User"> <constructor-arg index="0" value="李四"/> </bean> <!-- type创建方式,必须定义有参构造器 --> <bean id="user" class="com.baidu.pojo.User"> <constructor-arg type="java.lang.String" value="王五"/> </bean> <!-- name创建方式,必须定义set方法 --> <bean id="user" class="com.baidu.pojo.User"> <constructor-arg name="name" value="阿达"/> </bean>
-
set注入(重点)
<!-- 默认创建方式,必须定义set方法,会调用无参构造器 --> <bean id="user" class="com.baidu.pojo.User"> <property name="name" value="张三"/> </bean> <bean id="student" class="com.baidu.pojo.Student"> <property name="name" value="张三"/> <property name="address" ref="address"/> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> </array> </property> <property name="hobbies"> <list> <value>听歌</value> <value>跑步</value> </list> </property> <property name="card"> <map> <entry key="idCard" value="111111222222223333"/> <entry key="bankCard" value="123456789"/> </map> </property> <property name="games"> <set> <value>LOL</value> <value>COC</value> <value>BOB</value> </set> </property> <property name="wife"> <null/> </property> <property name="info"> <props> <prop key="学号">1001</prop> <prop key="性别">0</prop> </props> </property> </bean>
-
扩展注入,可以简化xml配置
- p-namespace(需要引入xml约束(语法:p:name=“张三”))
- c-namespace(需要引入xml约束(语法:c:name=“张三”))
<!-- p-namespace --> <bean id="user" class="com.baidu.pojo.User" p:name="张三" p:age="20"/> <!-- c-namespace --> <bean id="user" class="com.baidu.pojo.User" c:name="李四" c:age="18"/>
-
注入外部bean(set注入)
<bean id="userDaoImpl" class="com.baidu.dao.UserDaoImpl"/> <bean id="userDaoMysqlImpl" class="com.baidu.dao.UserDaoMysqlImpl"/> <bean id="userServiceImpl" class="com.baidu.service.UserServiceImpl"> <property name="userDao" ref="userDaoMysqlImpl"/> </bean>
public class UserServiceImpl implements UserService{ private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
-
注入内部bean也叫嵌套bean(set注入),以及级联赋值
<!-- 内部bean --> <bean id="user" class="com.baidu.pojo.User"> <!-- 设置普通属性 --> <property name="name" value="张三"/> <!-- 设置对象类型属性 --> <property name="city"> <bean id="city" class="com.baidu.pojo.City"> <property name="name" value="北京"/> </bean> </property> </bean> <!-- 内部bean-级联赋值(也可以通过外部bean实现,了解一下) --> <bean id="city" class="com.baidu.pojo.City"> <property name="name" value="北京"/> </bean> <bean id="user" class="com.baidu.pojo.User"> <!-- 设置普通属性 --> <property name="name" value="张三"/> <!-- 设置对象类型属性 --> <property name="city" ref="city"/> <!-- 设置对象中的属性(基于该属性的get方法) --> <property name="city.name" value="上海"/> </bean>
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User user = context.getBean("user", User.class); System.out.println(user.getName()); System.out.println(user.getCity().getName()); }
-
提取list等类型属性注入(需要引入util约束,语法:
<util:list/>
,了解即可,此处省略) -
Bean的作用域:scope
- 单例模式(默认)
- singleton
- 原形模式
- prototype
- 其他(web项目中才会用)
- request
- session
- application
<!-- scope 作用域 --> <bean id="user" class="com.baidu.pojo.User" c:name="李四" c:age="18" scope="singleton"/>
- 单例模式(默认)
Bean的自动装配
-
在XML中显式装配
<!-- 自动装配 autowired --> <bean id="user" class="com.baidu.pojo.User" autowire="byName"> <property name="name" value="张三"/> </bean> <bean id="user" class="com.baidu.pojo.User" autowire="byType"> <property name="name" value="张三"/> </bean>
-
在Java中显式装配
- 在xml中定义bean,Java中配置自动装配
<!-- 开启注解支持 --> <context:annotation-config/> <!-- 定义类 --> <bean id="dog" class="com.baidu.pojo.Dog"/> <bean id="cat" class="com.baidu.pojo.Cat"/> <bean id="user" class="com.baidu.pojo.User"/>
public class User { @Nullable // 表示可以为空 private String name; @Autowired // 表示开启自动装配 @Nullable // 表示可以为空 private Dog dog; @Autowired(required = false) // 开启自动装配,并且可以为空 @Qualifier(value = "cat") // 指定bean-id private Cat cat; }
- 在xml中开启扫描,Java中通过注解自动注册bean并配置自动装配
<!-- 指定要扫描的包 --> <context:component-scan base-package="com.baidu.pojo"/> <!-- 开启注解支持 --> <context:annotation-config/>
@Component // 表示该类被Spring托管了,就是bean @Scope("prototype") // 作用域,选项参考xml配置文件方式singleton、prototype。。。 public class User { @Value("张三") // 表示默认值是张三 private String name; }
- 在xml中定义bean,Java中配置自动装配
-
隐式装配
- @Autowired
- byName
- 识别的是bean-id
- byType
- 识别的是bean-class
- 其他
- byName
- 使用注解
- 导入约束:context
xmlns:context="http://www.springframework.org/schema/context"
- 配置注解支持:
<context:annotation-config/>
- 类中添加:@Autowired
- 可以在属性上使用,也可以在set方法上使用,如果在属性上使用就可以省去set方法
- 其他注解
- @Nullable
@Nullable // 表示可以为空
- 表示这个字段可以为空,等价于:@Autowired(required = false)
- @Qualifier
@Qualifier(value = "cat") // 指定bean-id
- @Nullable
- 导入约束:context
- @Resource注解
- @Resource和@Autowired区别
- @Autowired通过byType实现,必须要求这个对象存在
- @Resource默认通过byName实现,如果找不到名字则按byType实现,否则报错
- 用法:
@Resource(name="user")
- @Resource和@Autowired区别
- @Autowired
使用注解开发
- 前提
<?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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!-- 指定要扫描的包 --> <context:component-scan base-package="com.baidu.pojo"/> <!-- 开启注解支持 --> <context:annotation-config/> </beans>
- 获取容器方式
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
- bean
- @Component
- 放在类上边,表示该类被Spring管理了,就是bean
- @Component
- 属性如何注入
- @Value(“张三”)
- 放在属性上边,表示该属性的默认值为“张三”
- 也可以放在set方法上
- @Value(“张三”)
- 衍生的注解
- @Component注解有几个衍生注解,在web开发中会按MVC三层架构分层
- dao【@Repository】
- service【@Service】
- controller【@Controller】
- 以上四个注解效果都是一样的,都是讲类注册到Spring中,装配bean
- @Component注解有几个衍生注解,在web开发中会按MVC三层架构分层
- 自动装配(上边已经讲过)
- @Autowired
- @Qualifier
- @Nullable
- @Resource
- @Autowired
- 作用域
- @Scope(“prototype”)
- 选项参考xml配置文件方式singleton、prototype。。。
- 默认还是singleton
- @Scope(“prototype”)
- 小结
- XML于注解
- XML更加智能,适用于任何场合,维护方便
- 注解不是自己的类用不了,维护相对复杂
- XML于注解最佳实践
- XML用来管理bean
- 注解只负责完成属性的注入
- XML于注解
使用Java方式配置Spring
- 完全不用Spring的XML配置了,全权交给Java来做
- JavaConfig是Spring的一个子项目,在Spring4之后成为了核心功能
- 获取容器方式
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
- 主要注解
- @Configuration
- 相当于之前的beans.xml
- @ComponentScan(“com.baidu.config”)
- 用法类似于beans.xml
- @Import(MyConfig2.class)
- 合并其他config类
- @Bean
- 相当于beans.xml中的bean,方法名相当于id,返回值相当于class
- @Component
- 标记类被Spring托管
- @Configuration
- 核心代码
@Configuration // 相当于之前的beans.xml @ComponentScan("com.baidu.config") // 用法类似于beans.xml @Import(MyConfig2.class) // 合并其他config类 public class MyConfig { @Bean // 相当于beans.xml中的bean,方法名相当于id,返回值相当于class public User getUser(){ return new User(); } } @Component public class User { @Value("张三") private String name; } @Test public void test01(){ ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); User user = (User) context.getBean("getUser"); }
代理模式
- 这个是SpringAOP的底层【SpringAOP和SpringMVC】
- 静态代理
- 角色
- 抽象角色,一般会使用接口或抽象类解决
- 真实角色,被代理角色
- 代理角色,代理真实角色,代理后一般会做一些附属操作
- 客户角色,访问代理对象的人
- 核心代码(房东租房案例)
// 抽象角色,接口 public interface Rent { // 租房 void rent(); } // 被代理类,房东身份 public class Host implements Rent { public void rent() { System.out.println("房东要出租房。。。"); } } // 代理角色,中介身份 public class Proxy implements Rent { private Host host; public Proxy() {} public Proxy(Host host) { this.host = host; } public Host getHost() { return host; } public void setHost(Host host) { this.host = host; } public void rent() { seeHouse(); host.rent(); sign(); } public void seeHouse(){ System.out.println("中介带看房。。。"); } public void sign(){ System.out.println("中介签合同。。。"); } } // 客户角色 public class Client { public static void main(String[] args) { Proxy proxy = new Proxy(new Host()); proxy.rent(); } }
- 核心代码(添加日志案例)
public interface UserService { void add(); void delete(); void update(); void query(); } public class UserServiceImpl implements UserService { public void add() { System.out.println("增加了一个用户。。。"); } public void delete() { System.out.println("删除了一个用户。。。"); } public void update() { System.out.println("修改了一个用户。。。"); } public void query() { System.out.println("查询了一个用户。。。"); } } public class UserServiceProxy implements UserService { private UserServiceImpl userService = new UserServiceImpl(); public UserServiceProxy() {} public UserServiceProxy(UserServiceImpl userService) { this.userService = userService; } public void add() { log("add"); userService.add(); } public void delete() { log("delete"); userService.delete(); } public void update() { log("update"); userService.update(); } public void query() { log("query"); userService.query(); } public void log(String action) { System.out.println("[debug]调用了" + action + "方法。。。"); } }
- 角色
- 动态代理
- 静态代理和动态代理的角色一样
- 动态代理的类是自动生成的,不是我们直接写好的
- 动态代理分为两大类
- 基于接口的:JDK【本案例使用】
- 基于类的:cglib
- Java字节码:javassist(一个日本人发明的)
- 需要了解两个类
- Proxy:代理
- InvocationHandler:调用处理程序
- 主要逻辑
- 创建一个自动代理类来实现InvocationHandler
- 声明一个属性(被代理的接口)并增加set方法
- 声明一个返回代理类的方法
- 实现invoke方法,处理代理实例并返回结果
- 调用者
- 定义被代理对象
- 实例化自动代理工具类
- 注入被代理对象
- 生成代理对象
- 调用代理对象里的方法
- 创建一个自动代理类来实现InvocationHandler
- 核心代码
// 我的自动代理类 public class MyProxyInvocationHandler 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()); return method.invoke(target, args); } public void log(String action) { System.out.println("执行了" + action + "方法"); } } // 调用者 public class Client { public static void main(String[] args) { // 被代理对象 Host host = new Host(); // 自动代理工具类 MyProxyInvocationHandler handler = new MyProxyInvocationHandler(); // 注入被代理对象 handler.setTarget(host); // 生成代理对象 Rent proxy = (Rent) handler.getProxy(); // 调用代理对象里的方法 proxy.rent(); } }
AOP
- 什么是AOP
- AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- AOP在Spring中的作用
- 提供声明式事务,允许用户自定义切面
- 横切关注点
- 切面(Aspect)
- 通知(Device)
- 目标(Target)
- 代理(Proxy)
- 切入点(PointCut)
- 连接点(JoinPoint)
- 使用Spring实现AOP
- 前提
- pom.xml中添加依赖包:aspectjweaver
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency>
- beans.xml中添加aop约束
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd ">
- pom.xml中添加依赖包:aspectjweaver
- 方式一:使用SpringApi接口
- 创建类,实现AOP通知接口(MethodBeforeAdvice、AfterReturningAdvice、AfterAdvice…)
public class MyAfterLog implements AfterAdvice { // returnValue:返回值 // method:要执行的目标对象的方法 // args:参数 // target:目标对象 public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("【后置log】" + target.getClass().getName() + "的" + method.getName() + "被执行了。。。返回结果为:" + returnValue); } }
- 在beans.xml中注册bean、配置aop(切入点、执行环绕…)
<!-- 注册bean --> <bean id="userService" class="com.baidu.service.UserServiceImpl"/> <bean id="beforeLog" class="com.baidu.log.MyBeforeLog"/> <bean id="afterLog" class="com.baidu.log.MyAfterLog"/> <!-- 方式一:使用原生的Spring API接口 --> <!-- 配置aop:需要导入aop的约束 --> <aop:config> <!-- 切入点:expression表达式:execution(要执行的位置*****) --> <aop:pointcut id="pointcut" expression="execution(* com.baidu.service.UserServiceImpl.*(..))"/> <!-- 执行环绕增加 --> <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config>
- 创建类,实现AOP通知接口(MethodBeforeAdvice、AfterReturningAdvice、AfterAdvice…)
- 方式二:自定义类实现
- 创建自定义通知类
public class MyPoint { public void before() { System.out.println("——————before被执行了——————"); } public void after() { System.out.println("——————after被执行了——————"); } }
- 在beans.xml中注册bean、配置aop(自定义切面、切入点、通知…)
<!-- 注册bean --> <bean id="userService" class="com.baidu.service.UserServiceImpl"/> <!-- 方式二:使用其定义类 --> <bean id="myPoint" class="com.baidu.diy.MyPoint"/> <!-- 配置aop:需要导入aop的约束 --> <aop:config> <!-- 自定义切面:ref=要引用的类 --> <aop:aspect ref="myPoint"> <!-- 切入点 --> <aop:pointcut id="point" expression="execution(* com.baidu.service.UserServiceImpl.*(..))"/> <!-- 通知 --> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config>
- 创建自定义通知类
- 方式三:使用注解
- 创建自定义通知类
@Component // 注册bean @Aspect // 注册切面 public class MyAnnotationPoint { // 定义切入点 @Before("execution(* com.baidu.service.UserServiceImpl.*(..))") public void before() { System.out.println("——————before——————"); } }
- 在beans.xml中context开启注解支持、context指定要扫描的包、aop开启注解支持
<!-- 注册bean --> <bean id="userService" class="com.baidu.service.UserServiceImpl"/> <!-- 方式三:使用注解 --> <!-- context开启注解支持 --> <context:annotation-config/> <!-- context指定要扫描的包 --> <context:component-scan base-package="com.baidu"/> <!-- aop开启注解支持:基于以上两个context配置 --> <aop:aspectj-autoproxy/>
- 创建自定义通知类
- 前提
集成Mybatis
- 本章单独文档了。