Spring5-基础知识
笔记简介:
1、Spring概念
2、IOC
3、AOP
4、JDBCTemplate
5、事务管理
6、Spring5里边的新特性
概述:
1、Spring框架是一个轻量级 开源的javaEE框架。
轻量级:引入依赖的jar包数量少,体积小。不再需要依赖其他的组件,可以单独使用。
(1)IOC:控制反转。不再new对象,而是把创建对象的过程交给Spring,进行管理。
(2)AOP:面向切面编程。不再修改源代码进行功能的修改增强。
特点:
1、方便解耦,简化开发
2、AOP编程支持
3、方便程序的测试,整合Junit
4、方便集成各种框架
5、支持事务的管理
6、源码经典
入门案例-Spring创建对象
下载Spring5的jar包
5.2.6GA稳定版本 https://repo.spring.io
https://repo.spring.io/ui/native/plugins-release/org/springframework/spring/5.2.0.M3/
1、准备5个jar包
commons-logging-1.1.1.jar
spring-beans-5.2.0.M3.jar
spring-context-5.2.0.M3.jar
spring-core-5.2.0.M3.jar
spring-expression-5.2.0.M3.jar
2、测试代码
<?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类的对象的创建 --><bean id="user" class="com.pshdhx.User"></bean>
</beans>
package com.pshdhx;import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @Auther: pshdhx* @Date: 2023/08/24/14:50* @Description:*/
public class TestDemo {@Testpublic void testAdd(){/*** 1、加载spring的配置文件* 2、获取配置的对象*/ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");User user = context.getBean("user",User.class);System.out.println("user = " + user);user.add();}
}
IOC容器
底层原理
1、控制反转:把对象创建和对象之前的调用过程,交给Spring进行管理。
2、降低耦合度
主要用到了xml的解析、工厂设计模式、反射。
工厂模式:两个类之前的调用,多了一个工厂类
class UserFactory{public static UserDao getDao(){return new UserDao();}
}
IOC过程:
1、xml配置文件,配置要创建的对象
<bean id="userDao" class="com.pshdhx.UserDao"></bean>
2、工厂类
class UserFactory{public static UserDao getDao(){String classValue = class属性值; //xml解析Class clazz = Class.forName(classValue);// 通过反射创建对象 return (UserDao)clazz.newInstance(); }
}
降低耦合度:比如说UserDao的路径改变了,用之前的方式,需要都要改,用现在的方式,只需要改配置文件即可。
BeanFactory接口:内部使用接口,不提供开发人员使用。加载配置文件的时候,不会创建对象。在使用的时候,采取创建对象。
ApplicationContext接口:BeanFactory的子接口,功能更强大,一般面向开发人员使用。在加载配置文件的时候,就会创建配置的对象。
ApplicationContext的实现类:
FileSystem是盘符路径
classPath是工程路径
IOC的Bean管理
1、spring创建对象
2、spring注入属性【==get set方法】
IOC接口 BeanFactory
IOC的bean管理的具体操作,基于xml
创建对象的时候,默认也是执行无参构造方法,完成对象的创建。如果User对象中是有参构造,那么Spring创建对象报错。
DI:依赖注入。
1、使用set方法注入
public class User {private String username;public void setUsername(String username) {this.username = username;}public void add(){System.out.println("this.username=="+this.username);}
}
<!-- 配置User类的对象的创建 -->
<bean id="user" class="com.pshdhx.User"><property name="username" value="pansd"/>
</bean>
2、使用构造方法注入
public class User {private String username;public void setUsername(String username) {this.username = username;}public User(String username) {this.username = username;}public void add(){System.out.println("this.username=="+this.username);}
}
<!-- 配置User类的对象的创建 --><bean id="user" class="com.pshdhx.User">
<!-- <property name="username" value="pansd"/>--><constructor-arg name="username" value="pshdhx"></constructor-arg></bean>
P空间注入
xmlns:p="http://www.springframework.org/schema/p"<bean id="user" class="com.pshdhx.User" p:username="pshdhx">
<!-- <property name="username" value="pansd"/>-->
<!-- <constructor-arg name="username" value="pshdhx"></constructor-arg>--></bean>
注入其他类型
<property name="username"><null/>
</property><property name="username"><value> <![CDATA[<<pshdhx>>]]</value>
</property>
注入外部Bean
<bean id="addressImpl" class="com.pshdhx.Address"></bean>
<bean id="user" class="com.pshdhx.User" ><property name="address" ref="addressImpl"/>
</bean>
public class User {private Address address;public void setAddress(Address address) {this.address = address;}
}
注入内部bean和级联赋值
1、一对多关系【部门和员工】
<bean id="emp" class="com.pshdhx.Emp"><property name="ename" value="pshdhx"/><property name="gender" value="male"/><property name="dept"><bean id="dept" class="com.pshdhx.Dept"><property name="dname" value="IT"/></bean></property>
</bean>
注入集合:Arr、List、Set、Map
<bean id="student" class="com.pshdhx.Student"><property name="courses"><array><value>java</value><value>DB</value></array></property><property name="list"><list><value>list1</value><value>list2</value></list></property><property name="set"><set><value>set1</value><value>set2</value></set></property><property name="map"><map><entry key="key1" value="value1"></entry><entry key="key2" value="value2"></entry></map></property>
</bean>
集合对象为对象
<property name="courseList"><list><ref bean="course1"></ref><ref bean="course2"></ref></list></property><bean id="course1" class="com.pshdhx.Course"><property name="cname" value="Spring5"></property></bean><bean id="course2" class="com.pshdhx.Course"><property name="cname" value="SpringMVC"></property></bean>
抽取公用xml:引入名称空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:util="http://www.springframework.org/schema/util"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.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"><util:list id="bookList"><value>Spring</value><value>SpringMVC</value></util:list><bean id="book" class="com.pshdhx.Book"><property name="bookListString" ref="bookList"></property></bean>
IOC的bean管理的具体操作
Spring里边有普通的Bean,还有另外一种Bean-FactoryBean
普通Bean特点:在xml中class定义的是什么类型,那么getBean返回的就是什么类型。
工厂Bean特点:定义类型和返回类型可以不一致。
package com.pshdhx.factoryBean;import com.pshdhx.Course;
import org.springframework.beans.factory.FactoryBean;/*** @Auther: pshdhx* @Date: 2023/08/28/13:27* @Description:*/
public class MyBean implements FactoryBean {@Overridepublic Object getObject() throws Exception {Course course = new Course();course.setCname("Java");return course;}@Overridepublic Class<?> getObjectType() {return null;}@Overridepublic boolean isSingleton() {return false;}
}
@Test
public void testMyBean(){ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");Course myBean = context.getBean("myBean", Course.class);System.out.println("myBean = " + myBean);
}
Bean的作用域
作用域:是单实例还是多实例,默认是单实例的。
getBean两次,看看对象是否是单实例。
bean标签行中,有个scope属性设置:singleton/prototype
特点:
singleton:单实例。加载spring配置文件的时候,创建单实例对象。
prototype:多实例。不是在加载Spring配置文件的时候创建对象,而是在getBean的时候,创建新的对象。
request:一次请求
session:一次会话
bean的生命周期
生命周期:从对象的创建到对象的销毁。
1、通过构造器创建Bean实例。(无参构造)
2、为Bean的属性值设置值和对其他Bean的引用(调用set方法的过程)。
将Bean的实例,传递给后置处理器的方法【初始化之前】
3、调用Bean里边的初始化的方法(需要进行配置)
将Bean的实例,传递给后置处理器的方法【初始化之后】
4、bean可以使用了,对象可以获取到了
5、当容器在关闭的时候,bean调用销毁的方法(需要进行配置)。
代码演示:
public class BeanInit {private String initName;public BeanInit() {System.out.println("第一步:无参构造");}public void setInitName(String initName) {this.initName = initName;System.out.println("第二步:set设置属性值");}public void initMethod(){System.out.println("第三步:Bean初始化方法");}public void destoryMethod(){System.out.println("第五步:Bean销毁方法");}
}
@Test
public void testMyBean(){ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");BeanInit beanInit = context.getBean("beanInit", BeanInit.class);System.out.println("第四步:获取对象"+beanInit);context.close();
}
结果:
这个初始化的前置和后置方法,并没有和某一个bean做关联,所以说每个Bean的初始化之前和之后,都是会执行这个方法的。
public class BeforeAfterBeanInit implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行初始化方法之前");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行初始化方法之后");return bean;}
}
Bean管理-基于xml的自动装配
手动装配:bean标签中的
自动装配:根据指定的装配规则(属性类型和属性名称),spring会自动帮助我们完成注入。
演示自动装配过程:
Bean的id的值和类名必须一致。
byType:相同类型的Bean只能注册一个。
Bean管理-引入外部的属性文件
引入:druid.jar的包
引入context的名称空间
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd<context:property-placeholder location="classpath:jdbc.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="username" value="${db.username}"/><property name="password" value="${db.password}"/><property name="url" value="${db.url}"/><property name="driverClassName" value="${db.driverClassName}"/></bean>
基于注解实现创建对象和属性注入
注解:代码的特殊标记@注解名称(属性名=属性值,属性名=属性值)
注解:
1、可以在方法中、类中、属性中都可以加注解
2、目的:简化配置
@Component:
@Service:
@Controller
@Repository
(1) 需要额外引入aop的依赖。
(2)开启组件扫描 引入context
<context:component-scan base-package="com.pshdhx.annotation" ></context:component-scan>
(3)创建类,加注解
@Component
public class Bean1 {public void test(){System.out.println("annotation 创建对象");}
}
组件扫描细节:
<context:component-scan base-package="com.pshdhx.annotation" ></context:component-scan><context:component-scan base-package="com.pshdhx.annotation" use-default-filters="false"><!-- 只扫描带Controller注解的类 --><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan><context:component-scan base-package="com.pshdhx.annotation" ><!-- 不扫描带Controller注解的类 --><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/></context:component-scan>
基于注解实现属性注入
1、@Autowired:根据属性类型注入
2、@Qualifier:根据属性名称自动注入,配合autowired一块使用
3、@Resource:根据类型和名称进行注入
4、@Value
@Value(value="pshdhx")
private String name;
纯注解开发
//1、创建配置类、替代xml文件
@Configuration
@ComponentScan(basePackages={"com.pshdhx"})
public class SpringConfig{}
//编写测试类,不再加载xml文件了
public void test{ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);}
AOP
概念:对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间耦合度降低,提高程序的可重用性,同时提高开发效率。
AOP的使用场景——登录功能:例如添加权限,if 管理员 if普通用户。
AOP就是在不通过修改源代码的方式添加新的功能。
权限管理模块,独立出一个新的模块,配置到主干里边去。
底层原理:
使用到了动态代理,增强了类中某个方法的功能。
(1)有接口的情况:要使用到JDK的动态代理。
创建出代理对象,通过代理对象实现某些功能。
public class TestAopJdkProxy {public static void main(String[] args) {//创建UserDao的代理对象,利用这个对象进行功能的修改操作UserDao userDao = (UserDao) Proxy.newProxyInstance(TestAopJdkProxy.class.getClassLoader(), new Class[]{UserDao.class},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(method.getName() + "方法执行之前,参数是:"+ Arrays.toString(args));
// Object proxy 这个是object对象,没有什么东西Object result = method.invoke(new UserDaoImpl(), args);System.out.println("方法执行之后");return result;}});userDao.add(3, 4);}
}
(2)没接口的情况:要使用到CGLIB动态代理。
继承某个类,重写方法。【创建子类的代理对象】
术语
连接点:可以被增强的方法。
切入点:真正被增强的方法。
通知:实际增强的逻辑部分。【前置、后置、环绕、异常、最终通知】
切面:把通知应用到切入点的过程。
AOP操作
使用AspectJ工具实现AOP操作,单独的AOP框架,不是基于Spring的
1、基于注解的配置方式
(1)引入jar包
spring-aspects-5.2.0.M3.jar
cglib-2.2.jar
aspectjweaver-1.6.8.jar
aopalliance-1.0.jar
(2)切入点表达式,对哪个类中的哪个方法进行增强
execution([权限修饰符][返回类型][类的全路径][方法名字][参数列表])
execution(* com.pshdhx.aop.TestClass.add(..))
execution(* com.pshdhx.aop.TestClass.*(..))
execution(* com.pshdhx.aop.*.*(..))
after是方法之后执行
afterReturning是返回值返回之后执行
xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"
<context:component-scan base-package="com.pshdhx.aop.aspectj"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Component
@Aspect
public class UserProxy {@Before(value = "execution(* com.pshdhx.aop.aspectj.User.add(..))")public void before(){System.out.println("前置通知。。。。");}@After(value = "execution(* com.pshdhx.aop.aspectj.User.add(..))")public void after(){System.out.println("后置通知。。。。");}@Around(value = "execution(* com.pshdhx.aop.aspectj.User.add(..))")public void arround(ProceedingJoinPoint p) throws Throwable {System.out.println("环绕通知-前置环绕。。。。");p.proceed();System.out.println("环绕通知-后置环绕。。。。");}@AfterReturning(value = "execution(* com.pshdhx.aop.aspectj.User.add(..))")public void afterReturning(){System.out.println("afterReturning通知。。。。");}@AfterThrowing(value = "execution(* com.pshdhx.aop.aspectj.User.add(..))")public void afterThrowing(){System.out.println("afterThrowing通知。。。。");}
}
出现了异常之后,后置环绕通知和afterReturning接收不到。但是,around必须抛出异常,下边才能afterThrowing。
异常顺序:
环绕通知-前置环绕。。。。
前置通知。。。。
后置通知。。。。
afterThrowing通知。。。。
正常顺序:
环绕通知-前置环绕。。。。
前置通知。。。。
User 的add 方法
环绕通知-后置环绕。。。。
后置通知。。。。
afterReturning通知。。。。
切入点的抽取:
@AfterReturning(value = "pointCut()")public void afterReturning(){System.out.println("afterReturning通知。。。。");
}@Pointcut(value = "execution(* com.pshdhx.aop.aspectj.User.add(..))")
public void pointCut(){}
设置增强类的优先级:
一个方法被多次增强,可以在类上加一个注解@Order(1) 先执行 @Order(2)后执行
2、基于xm的配置方式
<!-- 以下是基于xml -->
<bean id="person" class="com.pshdhx.aop.xml.Person"></bean>
<bean id="personProxy" class="com.pshdhx.aop.xml.PersonProxy"></bean>
<aop:config><aop:pointcut id="p" expression="execution(* com.pshdhx.aop.xml.Person.add(..))"/><aop:aspect ref="personProxy"><aop:before method="before" pointcut-ref="p"/></aop:aspect>
</aop:config>
JdbcTemplate
Spring框架对JDBC进行了封装,使用JdbcTemplate方便实现对数据库的操作。
1、引入jar包
druid-1.1.9.jar
mysql-connector-java-5.1.8.jar
spring-jdbc-5.2.0.M3.jar
spring-orm-5.2.0.M3.jar
spring-tx-5.2.0.M3.jar
增删改操作:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
"><context:property-placeholder location="classpath:jdbc.properties"/><context:component-scan base-package="com.pshdhx.jdbctemplate"/><aop:aspectj-autoproxy></aop:aspectj-autoproxy><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="username" value="${db.username}"/><property name="password" value="${db.password}"/><property name="url" value="${db.url}"/><property name="driverClassName" value="${db.driverClassName}"/></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean></beans>
@Repository
public class UserDaoImpl implements UserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic int addUser(User user) {String sql = "insert user(user_id,username,user_status) values (?,?,?)";Object []args = {user.getUserId(),user.getUserName(),user.getUserStatus()};int update = jdbcTemplate.update(sql, args);return update;}
}
查询操作:某个值count(*),某条记录,多条记录的集合
public int findCount() {String sql = "select count(*) from user";Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);return integer;
}
查询某一条记录
public User findUserById(Integer id) {String sql = "select * from user where user_id = ?";Object []args = {id};User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), args);return user;
}
查询多条记录
@Override
public List<User> queryAllUser() {List<User> userL = jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<User>(User.class));return userL;
}
批量添加:
@Override
public void batchImport(List<Object []> userList) {String sql = "insert into user values(?,?,?)";jdbcTemplate.batchUpdate(sql,userList);
}List<Object[]> list = new ArrayList<>();Object[] u1 = {12,"12","0"};Object[] u2 = {13,"13","1"};Object[] u3 = {14,"14","1"};list.add(u1);list.add(u2);list.add(u3);userService.batchImport(list);
事务
事务特性:
原子性:要么都成功,要么都失败。
一致性:操作之前和之后,数据一致。
隔离性:多个事务操作,不会产生影响。
持久性:事务提交之后,存库。
声明式事务管理
<!-- 创建事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean><!-- 开启事务注解1、引入名称空间 tx2、开启事务注解3、在service类中添加注解 @Transactional 可以加到类上边(为类中所有方法添加事务),也可以加到方法上边--><tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
propagation:事务的传播行为
【一个事务的方法被另外一个事务的方法调用,该如何进行处理?】
1、required传播级别:如果有事务正在运行,当前的方法就在这个事务内运行。否则,就会启动一个新的事务,并在自己的事务内运行。
举例说明:类是事务A,方法是方法1,类是事务B,方法是方法2。方法1调用方法2,方法1会开启事务A,方法2会加入到事务A
2、required_new :当前的方法必须启动新事务,并在它自己的事务内运行。如果有事务正在运行,应该将它挂起。
举例说明:内层事务和外层事务互不影响。
3、supports:如果有事务在运行,当前的方法就在这个事务内运行。否则,它可以不运行在事务中。
举例:【方法A单独的调用方法B,如果方法A没有事务,方法B可以不运行在事务中】
ioslatin:事务的隔离级别
多事务操作之间不会产生影响
脏读:读取到了未提交事务的数据。【不提交可能会回滚】
不可重复读:一个未提交事务,读取到了一个已提交事务修改的数据。
幻读:一个未提交事务,读取到了一个已提交事务添加的数据。
脏读 | 不可重复度 | 幻读 | |
---|---|---|---|
读未提交 | 有 | 有 | 有 |
读已提交 | 无 | 有 | 有 |
可重复度[Mysql默认] | 无 | 无 | 有 |
串行化 | 无 | 无 | 无 |
@Service
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.REPEATABLE_READ)
public class UserService{}
事务的其他参数
1、timeout:事务需要在一定的时间内进行提交。如果不提交,事务需要进行回滚。默认值-1,是不超时的意思。timeout=5,是五秒的意思。
2、readOnly:是否只读。默认值是false【可以查询,可以增删改】,当设置成true之后,只能进行查询操作。
3、rollbackFor:回滚【设置哪些异常可以进行回滚】
4、noRollbackFor:【设置哪些异常不进行异常的回滚】
@Configuration
@ComponentScan(basePackages = "com.pshdhx")
@EnableTransactionManagement
public class TxConfig {@Beanpublic DruidDataSource getDruidDataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setUsername("root");dataSource.setPassword("pshdhx");dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/user_db");return dataSource;}@Beanpublic JdbcTemplate getJdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}//创建事务管理器@Beanpublic DataSourceTransactionManager getDataSourceTransactionManager(DataSource datasource){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(datasource);return dataSourceTransactionManager;}
}
整合日志框架
引入jar包
log4j-core-2.11.2.jar
log4j-slf4j-impl-2.11.2.jar
slf4j-api-1.7.30.jar
log4j-api-2.11.2.jar
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 日志级别以及优先级顺序 OFF >FATAL > ERROR > WARN > INFO > DEBUG >TRACE >ALL -->
<!-- Configuration 后面的status 用于设置log4j2自身内部信息的额输出 可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出 -->
<configuration status="INFO"><appenders><console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} -%msg%n"/></console></appenders><loggers><root level="info"><appender-ref ref="Console"/></root></loggers>
</configuration>
import com.mysql.jdbc.log.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @Auther: pshdhx* @Date: 2023/09/09/14:49* @Description:*/
public class UserLogger {private static final Logger log = LoggerFactory.getLogger(UserLogger.class);public static void main(String[] args) {log.info("测试日志");}
}