Spring
理念
使现有技术更加容易使用,具有强大的向后兼容性,庞大的生态系统,整合了现有的技术框架
官方文档
https://docs.spring.io/spring-framework/docs/current/reference/html/index.html
优点
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级的、非侵入式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务处理,支持框架整合
组成
扩展
- Spring Boot
- 一个快速开发的脚手架
- 基于Spring Boot可以快速开发单个微服务
- 约定大于配置
- Spring Cloud
- 基于Spring Boot实现
弊端
配置繁琐
IOC
控制反转是一种通过描述(xml或者注解)并通过第三方去生产或者获取对象的方式,在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入
原型
之前:程序根据用户需求主动创建对象
后来:使用注入,使程序成为被动的接受对象,大大降低耦合度,可以专注于业务的实现
package com.shaem.service;
import com.shaem.dao.User;
import com.shaem.dao.UserMysql;
public class UserServiceImp implements UserService {
private User user; //调用数据访问层
public void setUser(User user){
this.user=user;
}
public void getUser() {
user.getUser(); //业务层调用数据访问层
}
}
import com.shaem.dao.UserMysql;
import com.shaem.dao.UserOracle;
import com.shaem.service.UserService;
import com.shaem.service.UserServiceImp;
public class Test {
public static void main(String[] args) {
//用户调用业务层,不需要使用Dao层
UserService userService = new UserServiceImp();
((UserServiceImp)userService).setUser(new UserOracle());
userService.getUser();
((UserServiceImp)userService).setUser(new UserMysql());
userService.getUser();
}
}
dao层中有两个实现类
本质
控制反转是一种设计思想,DI(依赖注入)为IOC的一种实现方法,获得依赖对象的方式发生改变
对象由Spring来创建、装配、管理
创建对象的方式
- 使用无参构造创建对象
- 如果使用有参构造创建对象
HelloSpring
- 创建Bean
package com.shaem.pojo;
public class HelloSpring {
private String str;
public String getStr(){
return str;
}
public void setStr(String str){
this.str=str;
}
@Override
public String toString() {
return "HelloSpring{" +
"str='" + str + '\'' +
'}';
}
}
- 配置文件
<?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="hello" class="com.shaem.pojo.HelloSpring">
<property name="str" value="Spring" />
</bean>
</beans>
- 测试
public class Test {
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Bean.xml");
//我们的对象全交由Spring管理,使用时在Context中取出
Object hello = applicationContext.getBean("hello");
System.out.println(hello.toString());
}
}
实际操作
正常的Java创建对象
HelloSpring hello = new HelloSpring();
通过Spring来创建对象
<bean id="hello" class="com.shaem.pojo.HelloSpring">
<property name="str" value="Spring" />
</bean>
id=变量名
class = new 的对象
property给对象中的属性赋值
依赖注入
可以分为两个词来看,依赖
:对象依赖于Spring容器来创建,注入
:属性由Spring容器注入
注入方式
构造器注入
<bean id="" class="">
<constructor-arg name="" value=""></constructor-arg>
</bean>
使用
set注入
package com.shaem.pojo;
import java.util.*;
public class SetInjection {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,Integer> map;
private Set<String> set;
private Properties properties;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public Map<String, Integer> getMap() {
return map;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "SetInjection{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", map=" + map +
", set=" + set +
", properties=" + properties +
'}';
}
}
<bean id="address" class="com.shaem.pojo.Address"/> <bean id="setInjection" class="com.shaem.pojo.SetInjection"><!-- 普通注入--> <property name="name" value="陈琛琛"></property><!-- bean注入,使用ref--> <property name="address" ref="address"></property><!-- 数组注入--> <property name="books"> <array> <value>神奇的猫咪</value> <value>奇怪的狗狗</value> </array> </property><!-- 集合注入--> <property name="hobbys"> <list> <value>逛B站</value> <value>听歌</value> </list> </property><!-- map注入--> <property name="map"> <map> <entry key="刘芮峥" value="1"></entry> </map> </property> <property name="properties"> <props> <prop key="number">418240104</prop> <prop key="name">陈琛琛</prop> </props> </property> </bean>
拓展注入(需要导入相关的依赖)
- P命名空间
<bean id="" class="" p:xx="" p:xx=""></bean>
简化属性注入
- C命名空间
<bean id="" class="" c:xx="" c:xx=""></bean>
构造器属性注入
Bean作用域
- singleton 默认为单例默认,即不管创建多少个对象,拿到的全部为同一个
- prototype 原型 每次创建的对象都不同
自动装配
Spring会在上下文自动寻找,给bean装配属性
- 在xml文件中进行Bean的属性装配
<bean id="cat" class="com.shaem.pojo.Cat"></bean> <bean id="dog" class="com.shaem.pojo.Dog"></bean> <bean id="people" class="com.shaem.pojo.People"> <property name="name" value="陈琛琛"></property> <property name="cat" ref="cat"></property> <property name="dog" ref="dog"></property> </bean>
-
使用java进行装配(暂时不用)
-
隐式的自动装配
- byName(名字不同则无法自动装配)
<bean id="cat" class="com.shaem.pojo.Cat"></bean> <bean id="dog" class="com.shaem.pojo.Dog"></bean><!-- 在容器上下文中查找,查找people中set方法名字对应的bean,如有setdog,setcat--> <bean id="people" class="com.shaem.pojo.People" autowire="byName"> <property name="name" value="陈琛琛"></property> </bean>
- byType(必须只能有一个,不能有两个相同的class)
<bean id="cat1" class="com.shaem.pojo.Cat"></bean> <bean id="dog" class="com.shaem.pojo.Dog"></bean><!-- 在容器上下文中查找,查找people于属性类型相同的bean,如有Dog dog,Cat cat--> <bean id="people" class="com.shaem.pojo.People" autowire="byType"> <property name="name" value="陈琛琛"></property> </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 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
private String name; @Autowired private Dog dog; @Autowired private Cat cat;
总结:
- 先通过byType,如果类型大于1,则使用byName
- 可以省略set方法
常用注解
@Autowired
隐性注入对象,先通过byType,如果类型大于1,则使用byName
@Resource
隐性注入对象,先通过byName,如果名字不同,则使用byType
@Component
代表该类成为一个Bean
@Componentpublic class Student { @Value("程晨晨") private String name; @Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; }}
<context:component-scan base-package="com.shaem.pojo"/>
- dao层 @Repository
- service @Service
- controller @Controller
根据不同的层进行不同的注解标识,但是作用都是相等的
@scope(“”)
作用域
Java配置Spring
JavaConfig在Spring4之后成为推荐使用的注入方式
使用Bean注解在配置文件中创建
@Configuration
public class SHAEMConfig {
@Bean
public User getUser(){
return new User();
}
}
AOP
代理模式
静态代理
房东出租房子,我想要租房子,我可以通过中介找不同的房子,中介对我从房东那里租房子做了一些扩张功能,比如可以看多个不同房东的房子,签约等
- 抽象对象:抽象类或者接口,如租房这个抽象的概念
- 真实对象:被代理的对象
- 代理对象:代理真实对象,可以做一些功能上的扩展
- 客户对象:访问代理对象
好处:让真实业务更加存粹,专注于自身业务,可以进行一些功能上的扩展
坏处:每次都只能实现一个类,增加代码量
动态代理
动态代理的代理类是动态生成,分成两大类:
- 基于接口的动态代理
- JDK的动态代理(基于反射)
package com.shaem.pojo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandle implements InvocationHandler {
//被代理实现的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类,其中三个参数分别为类加载器,被代理的接口实例,实现了InvocationHandler的类并生成了实例
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//生成实例
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target,args); //rent为接口,可以传递参数进来,实现动态改变效果,当代理对象需要执行方法
return result;
}
}
public class Test {
@org.junit.Test
public void test(){
//真实角色
Host host = new Host();
//代理角色,但还未创建
ProxyInvocationHandle pih = new ProxyInvocationHandle();
//调用程序处理程序来处理我们要调用的接口
pih.setRent(host);
//动态生成代理角色
Rent proxy = (Rent)pih.getProxy();
//调用invoke方法,返回接口中定义的被代理对象处理好的方法
proxy.rent();
}
}
- 基于类的动态代理
- cjlib
优点
- 代理一类业务,可以根据接口直接动态生成代理类
- 公共业务扩展时,方便管理
- 业务分工
什么是AOP
面向切面编程,通过预编译方式和运行期动态代理实现功能的统一维护的一种技术
AOP实现方式一
使用Spring的API接口
<!-- 注册bean-->
<bean id="choose" class="com.shaem.service.ChooseIMP"></bean>
<bean id="log" class="com.shaem.log.Log"/>
<!-- 配置aop-->
<aop:config>
<!--pointcut:切入点 execution(要执行的位置,具体到方法)-->
<aop:pointcut id="pointcut" expression="execution(* com.shaem.service.ChooseIMP.*(..))"/>
<!--环绕增加 选择切入的方法,选择切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
@org.junit.Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理应该是接口类型,如果是类的话,就没有意义了
Choose choose = (Choose) context.getBean("choose");
choose.add();
}
AOP实现方式二
使用自定义类
<bean id="log2" class="com.shaem.log.Log2"/>
<aop:config>
<aop:aspect ref="log2">
<aop:pointcut id="point" expression="execution(* com.shaem.service.ChooseIMP.*(..))"/>
<aop:before method="before" pointcut-ref="point"></aop:before>
<aop:after method="after" pointcut-ref="point"></aop:after>
</aop:aspect>
</aop:config>
AOP实现方式三
使用注解
@Aspect //标注该类为一个切面
public class Log3 {
@Before("execution(* com.shaem.service.ChooseIMP.*(..))")
public void before(){
System.out.println("使用注解Before");
}
@After("execution(* com.shaem.service.ChooseIMP.*(..))")
public void after(){
System.out.println("使用注解After");
}
}
<!-- 开启注解支持-->
<bean id="log3" class="com.shaem.log.Log3"/>
<aop:aspectj-autoproxy/>