Spring5
文章目录
快速入门
1.导入maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
-
创建一个普通类,添加一个add方法
public class User { public void add(){ System.out.println("调用了add()方法"); } }
-
配置spring.xml文件 并且配置user对象
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置User对象 id:类名首字母小写 class:指定类路径 --> <bean id="user" class="com.lc.spring5.pojo.User"></bean> </beans>
-
测试
public class test { public static void main(String[] args) { // 从spring.xml配置文件中获取资源 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); //从容器中获取bean User user = context.getBean("user", User.class); System.out.println(user); user.add(); } } //测试结果:com.lc.spring5.pojo.User@e25b2fe // 调用了add()方法
工厂模式(解耦)
正常调用:
public class UserDao {
public void addUser(){
System.out.println("addUser()启动");
}
}
//Service层调用Dao层
public class UserService {
public void AddUser(){
new UserDao().addUser();
}
}
代码耦合度太高
应该降低耦合度:
public class UserDao {
public void addUser(){
System.out.println("addUser()启动");
}
}
//通过创建工厂来创建对象,降低了代码的耦合度
//(如果UserDao类名发生改变,我们只需要改变UserFactory中的代码即可 不用更改UserService中的代码)
public class UserFactory {
public static UserDao getUserDao(){
return new UserDao();
}
}
//Service层调用Dao层
public class UserService {
public void AddUser(){
UserDao userDao = UserFactory.getUserDao();
userDao.addUser();
}
}
Spring Ioc
-
介绍:控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
-
底层实现:工厂模式+反射
-
spring中提供了两种IOC实现方案
3.1 第一种是BeanFactory:它不是提供给我们开发者使用的,主要是提供给spring内部自己使用,加载配置文件的时候,不会根据文件的内容创建对象,当我们有需要的时候才回去创建。
3.2 第二种是ApplicationContext:主要是提供给开发者使用,对BeanFactory的实现有很对扩展功能,是BeanFactory的子接口。
加载配置文件的时候,会根据配置文件内容创建对象,并且缓存起来。
总结:相对来说,BeanFactory启动快,占用资源少,ApplicationContext启动慢,占用资源多
ApplicationContext下主要实现类
- ClassPathXmlApplicationContext:文件在类路径下的xml格式的配置文件(常用)
- FileSystemXmlApplicationContext:对应文件系统中的xml格式的配置文件(有权限问题,比如linux有些文件的权限不允许)
- WebApplicationContext:专门为WEB应用准备,它允许从相当于web根目录的路径中完成初始化工作
SpringBean的注入
在Spring中对象创建默认使用无参构造方法
*set注入对象
创建个实体类
@Data//lombok
public class People {
private String name;
private int age;
public People(){
System.out.println("调用了无参构造方法");
}
}
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="people" class="com.lc.spring5.pojo.People">
</bean>
</beans>
测试:
public class PeopleTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
People people = context.getBean("people", People.class);
people.setAge(11);
people.setName("雪饼");
System.out.println("people.name=="+people.getName()+"people.age=="+people.getAge());
}
}
/*运行结果:
调用了无参构造方法
people.name==雪饼people.age==11
*/
#####*有参构造注入
需要添加有参构造方法
public People(String name, int age) {
this.name = name;
this.age = age;
System.out.println("调用了有参构造方法");
}
更改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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="people" class="com.lc.spring5.pojo.People">
<!--第一种方式-->
<!-- <constructor-arg name="age" value="12"></constructor-arg>-->
<!-- <constructor-arg name="name" value="雪饼"></constructor-arg>-->
<!--第二种方式-->
<constructor-arg index="0" value="雪饼"></constructor-arg>
<constructor-arg index="1" value="12"></constructor-arg>
</bean>
</beans>
测试输出:
public class PeopleTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
People people = context.getBean("people", People.class); System.out.println("people.name=="+people.getName()+"people.age=="+people.getAge());
}
}
/**输出结果:
* 调用了有参构造方法
* people.name==雪饼people.age==12
*/
p标签注入(不常用了解即可)基于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"
<!--导入p标签的命名空间-->
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="people" class="com.lc.spring5.pojo.People" p:name="雪饼" p:age="123">
</bean>
</beans>
其他注入:
空值注入:
<property name="name">
<null></null>
</property>
注入特殊符号:
<property name="name">
<value><![CDATA[这里写特殊符号]]]></value>
</property>
*注入外部Bean对象
<!--通过ref来引用bean之外的bean对象-->
<bean id="userService" class="com.lc.spring5.service.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.lc.spring5.dao.UserDaoImpl"></bean>
*注入内部bean对象
<!--在一个bean对象中注入另一个bean对象-->
<bean id="teacher" class="com.lc.spring5.pojo.Teacher">
<property name="age" value="22"></property>
<property name="name" value="雪饼"></property>
<property name="student">
<bean id="student" class="com.lc.spring5.pojo.Student">
<property name="name" value="小雪"></property>
<property name="id" value="1"></property>
</bean>
</property>
</bean>
注入级联赋值:(我的理解就是以上方法的运用)
方法一:
<bean id="teacher" class="com.lc.spring5.pojo.Teacher">
<property name="age" value="22"></property>
<property name="name" value="雪饼"></property>
<property name="student" ref="student"></property>
</bean>
<bean id="student" class="com.lc.spring5.pojo.Student">
<property name="name" value="小雪"></property>
<property name="id" value="1"></property>
</bean>
方法二:
<bean id="teacher" class="com.lc.spring5.pojo.Teacher">
<property name="age" value="22"></property>
<property name="name" value="雪饼"></property>
<!--下面这个一定要写,不然会报空指针-->
<property name="student" ref="student"></property>
<property name="student.id" value="1">
</property>
<property name="student.name" value="小雪">
</property>
</bean>
<bean id="student" class="com.lc.spring5.pojo.Student">
</bean>
*注入集合类型的数值
<bean id="stu" class="com.lc.spring5.pojo.Stu">
<!--数组类型-->
<property name="arrays">
<array>
<value>array1</value>
<value>array2</value>
</array>
</property>
<!--List类型-->
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
</list>
</property>
<!--Map类型-->
<property name="map">
<map>
<entry key="map0" value="0"></entry>
<entry key="map1" value="1"></entry>
</map>
</property>
<!--Set类型-->
<property name="set">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
</bean>
注入集合的类型为对象:
<bean id="student01" class="com.lc.spring5.pojo.Student01">
<property name="lists">
<list>
<ref bean="book.java"></ref>
<ref bean="book.web"></ref>
</list>
</property>
</bean>
<bean id="book.java" class="com.lc.spring5.pojo.Book">
<property name="name" value="java"></property>
</bean>
<bean id="book.web" class="com.lc.spring5.pojo.Book">
<property name="name" value="web"></property>
</bean>
//运行结果
//Student01(lists=[Book(name=java), Book(name=web)])
使用util来对list进行赋值
<!--导入命名空间-->
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<!--使用util:list注入-->
<util:list id="list">
<value>list01</value>
<value>list02</value>
</util:list>
<bean id="stu" class="com.lc.spring5.pojo.Stu">
<property name="list" ref="list"></property>
</bean>
</beans>
*SpringBean的生命周期
简单分为五个步骤:
第一步:调用了无参构造
第二步:使用setName方法进行赋值
第三步:调用了init方法
第四步:使用myBean对象
第五步: 调用了destory方法
实操:
public class MyBean {
public MyBean() {
System.out.println("第一步:调用了无参构造");
}
private String name;
public void setName(String name) {
System.out.println("第二步:使用setName方法进行赋值");
this.name = name;
}
public void init(){
System.out.println("第三步:调用了init方法");
}
public void destory(){
System.out.println("第五步:调用了destory方法");
}
}
<!--自定义一个类继承BeanPostProcessor接口-->
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器在调用init之前执行操作");
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器在调用init之后执行操作");
return 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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myBean" class="com.lc.spring5.pojo.MyBean" init-method="init" destroy-method="destory">
<property name="name" value="雪饼"></property>
</bean>
<bean id="myBeanPostProcessor" class="com.lc.spring5.bean.MyBeanPostProcessor"></bean>
</beans>
测试:
public class MyBeanTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-08.xml");
MyBean myBean = context.getBean("myBean", MyBean.class);
System.out.println("第四步:使用myBean对象");
System.out.println(myBean);
context.close();
}
}
//输出结果:
/**
* 第一步:调用了无参构造
* 第二步:使用setName方法进行赋值
* 后置处理器在调用init之前执行操作
* 第三步:调用了init方法
* 后置处理器在调用init之后执行操作
* 第四步:使用myBean对象
* com.lc.spring5.pojo.MyBean@55d56113
* 第五步:调用了destory方法
*/
*Bean的作用域
默认情况下,Bean在Spring容器中是单例的,我们可以通过来配置Scope修改Bean的作用域
可以配置以下五个参数
类型: 说明:
singleton:在Spring容器中仅存在一个实例,即Bean以单例的形式存在
prototype:每次调用getBean()时,都会执行new操作,返回一个新的实例。
request:每次Http请求都会创建一个新的Bean。
session:同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。
globalSession:同一个全局的Session共享一个Bean,一般用于Portlet环境。
*IOC操作bean(注解方式)
Spring针对Bean管理中创建对象提供注解
@Component
@Service service层
@Controller controller层
@Repository dao层
使用注解创建一个:
-
导入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.2.1.RELEASE</version> </dependency>
-
导入命名空间
<?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:component-scan base-package="com.lc.spring5.pojo.aop"> </context:component-scan> </beans>
-
创建实体类:
//默认情况下value的值是 首字母小写的类名 @Component(value = "lc") public class Lc { private String name; }
-
测试:
public class LcTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-aop-01.xml"); Lc lc = context.getBean("lc", Lc.class); System.out.println(lc); } } //测试结果: com.lc.spring5.pojo.aop.Lc@1068e947
*使用注解注入
1.@Autowired:Autowired默认先按byType,如果发现找到多个bean,则,又按照byName方式比对,如果还有多个,则报出异常。
2.@Autowired 可以手动指定按byName方式注入,使用@Qualifier。
@Qualifier("userDao") 这两个一起用
3.@Resource 默认按 byName自动注入,如果找不到再按byType找bean,如果还是找不到则抛异常
4. @Value 对bean属性进行赋值
AOP
概念:在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
个人理解:在不改变原有代码的情况下,追加一些功能
*Aop底层原理:
-
有两种情况的动态代理:
第一种:有接口的情况,使用JDK动态代理
实现:创建接口的实现类,增强类的方法。
第二种:没有接口的情况,使用CGLIB动态代理
实现:继承子类的代理对象,增强类的方法。
*JDK动态代理实现:
//编写接口
public interface MyDao {
public int add(int a, int b);
}
//实现接口
public class MyDaoImpl implements MyDao{
public int add(int a, int b) {
System.out.println("add方法执行了");
return a+b;
}
}
//实现动态代理
public class JdkProxy {
public static void main(String[] args) {
Class[] interfaces = {MyDao.class};
MyDaoImpl myDao = new MyDaoImpl();
//主要是通过Proxy.newProxyInstance()来实现动态代理
MyDao dao = (MyDao)Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces, new MyDaoProxy(myDao));
int result = dao.add(1, 2);
System.out.println("result:"+result);
}
}
class MyDaoProxy implements InvocationHandler {
//把创建的是谁的代理对象,把谁传递过来
//有参构造传参
private Object obj;
public MyDaoProxy(Object obj) {
this.obj = obj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行之前...."+method.getName()+":传递的参数...."+ Arrays.toString(args));
Object invoke = method.invoke(obj, args);
System.out.println("方法执行之后..."+obj);
return invoke;
}
}
/** 结果:
方法执行之前....add:传递的参数....[1, 2]
add方法执行了
方法执行之后...com.lc.spring5.dao.MyDaoImpl@5e481248
result:3
*/
万能代理类(*):
//接口
public interface UserService {
public void addUser();
}
//接口实现类
public class UserServiceImpl implements UserService{
public void addUser(){
System.out.println("addUser被调用");
}
}
***********//代理工具类(重中之重)*********
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 + "方法");
}
}
//测试
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.addUser();
}
}
*使用动态代理的好处
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!
使用Spring实现Aop
导入依赖:
<!--导入aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
*方式一:
使用Spring的API接口
导入命名空间:
<?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.lc.spring5.dao.user">
</context:component-scan>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.lc.spring5.dao.user.UserDaoImpl.*(..))"/>
<aop:advisor advice-ref="beforePointCut" pointcut-ref="pointcut"></aop:advisor>
<aop:advisor advice-ref="afterPointCut" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
</beans>
//写个接口
@Service
public interface UserDao {
void addUser();
}
//接口实现类:
@Service
public class UserDaoImpl implements UserDao {
public void addUser() {
System.out.println("增加了一个用户");
}
//之前
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
@Component
public class BeforePointCut implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("before执行了");
}
}
//之后
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
@Component
public class AfterPointCut implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("afterReturning执行了");
}
}
//测试:
public class PointCutTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-aop-01.xml");
//动态代理代理的是接口,不要写成接口的实现类(UserDaoImpl) 否则会报错
UserDao userDaoImpl = context.getBean("userDaoImpl", UserDao.class);
System.out.println(userDaoImpl);
userDaoImpl.addUser();
}
}
//结果:
// com.lc.spring5.dao.user.UserDaoImpl@6aeb35e6
// before执行了
// 增加了一个用户
// afterReturning执行了
execution表达式:源码里的例子
For example : 'execution(* com.xyz.myapp.service.*.*(..))'
*方式二:
自定义类实现:
//写个接口
@Service
public interface UserDao {
void addUser();
}
//接口实现类:
@Service
public class UserDaoImpl implements UserDao {
public void addUser() {
System.out.println("增加了一个用户");
}
//自定义类
@Component
public class DIyPointCut {
public void before(){
System.out.println("前");
}
public void after(){
System.out.println("后");
}
}
配置
<context:component-scan base-package="com.lc.spring5.dao.user">
</context:component-scan>
<aop:config>
<!--自定义切面,ref 要引用的类-->
<aop:aspect id="diyPointCut" ref="DIyPointCut">
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.lc.spring5.dao.user.UserDaoImpl.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
*方式三:
注解实现:
//写个接口
@Service
public interface UserDao {
void addUser();
}
//接口实现类:
@Service
public class UserDaoImpl implements UserDao {
public void addUser() {
System.out.println("增加了一个用户");
}
//注解实现:
@Component
@Aspect
public class AnnocationPointCut {
@Before("execution(* com.lc.spring5.dao.user.UserDaoImpl.*(..))")
public void before(){
System.out.println("前");
}
@After("execution(* com.lc.spring5.dao.user.UserDaoImpl.*(..))")
public void after(){
System.out.println("后");
}
@Around("execution(* com.lc.spring5.dao.user.UserDaoImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
jp.proceed();
System.out.println("环绕后");
}
}
配置文件:
<!--开启扫描包-->
<context:component-scan base-package="com.lc.spring5.dao.user">
</context:component-scan>
<!--开启注解支持!
JDK(默认proxy-target-class="false)
cglib(proxy-target-class="true")
-->
<aop:aspectj-autoproxy/>
测试:
public class PointCutTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-aop-02.xml");
//动态代理代理的是接口,不要写成接口的实现类(UserDaoImpl) 否则会报错
UserDao userDaoImpl = context.getBean("userDaoImpl", UserDao.class);
System.out.println(userDaoImpl);
userDaoImpl.addUser();
}
}
/** 结果:
* 环绕前
* 前
* 增加了一个用户
* 环绕后
* 后
*/