笔记简介:
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、源码经典
下载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、测试代码
<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="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 {
@Test
public 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();
}
}
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是工程路径
1、spring创建对象
2、spring注入属性【==get set方法】
创建对象的时候,默认也是执行无参构造方法,完成对象的创建。如果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);
}
}
<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);
}
}
<bean id="user" class="com.pshdhx.User">
<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">
bean>
注入其他类型
<property name="username">
<null/>
property>
<property name="username">
<value>
<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>javavalue>
<value>DBvalue>
array>
property>
<property name="list">
<list>
<value>list1value>
<value>list2value>
list>
property>
<property name="set">
<set>
<value>set1value>
<value>set2value>
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:引入名称空间
<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.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<util:list id="bookList">
<value>Springvalue>
<value>SpringMVCvalue>
util:list>
<bean id="book" class="com.pshdhx.Book">
<property name="bookListString" ref="bookList">
property>
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 {
@Override
public Object getObject() throws Exception {
Course course = new Course();
course.setCname("Java");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public 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);
}
作用域:是单实例还是多实例,默认是单实例的。
getBean两次,看看对象是否是单实例。
bean标签行中,有个scope属性设置:singleton/prototype
特点:
singleton:单实例。加载spring配置文件的时候,创建单实例对象。
prototype:多实例。不是在加载Spring配置文件的时候创建对象,而是在getBean的时候,创建新的对象。
request:一次请求
session:一次会话
生命周期:从对象的创建到对象的销毁。
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 {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行初始化方法之前");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行初始化方法之后");
return bean;
}
}
手动装配:bean标签中的
自动装配:根据指定的装配规则(属性类型和属性名称),spring会自动帮助我们完成注入。
演示自动装配过程:
Bean的id的值和类名必须一致。
byType:相同类型的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">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<context:component-scan base-package="com.pshdhx.annotation" >
<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的使用场景——登录功能:例如添加权限,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() {
@Override
public 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动态代理。
继承某个类,重写方法。【创建子类的代理对象】
连接点:可以被增强的方法。
切入点:真正被增强的方法。
通知:实际增强的逻辑部分。【前置、后置、环绕、异常、最终通知】
切面:把通知应用到切入点的过程。
使用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的配置方式
<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>
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
增删改操作:
<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.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: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 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public 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>
<tx:annotation-driven transaction-manager="transactionManager">tx:annotation-driven>
【一个事务的方法被另外一个事务的方法调用,该如何进行处理?】
1、required传播级别:如果有事务正在运行,当前的方法就在这个事务内运行。否则,就会启动一个新的事务,并在自己的事务内运行。
举例说明:类是事务A,方法是方法1,类是事务B,方法是方法2。方法1调用方法2,方法1会开启事务A,方法2会加入到事务A
2、required_new :当前的方法必须启动新事务,并在它自己的事务内运行。如果有事务正在运行,应该将它挂起。
举例说明:内层事务和外层事务互不影响。
3、supports:如果有事务在运行,当前的方法就在这个事务内运行。否则,它可以不运行在事务中。
举例:【方法A单独的调用方法B,如果方法A没有事务,方法B可以不运行在事务中】
多事务操作之间不会产生影响
脏读:读取到了未提交事务的数据。【不提交可能会回滚】
不可重复读:一个未提交事务,读取到了一个已提交事务修改的数据。
幻读:一个未提交事务,读取到了一个已提交事务添加的数据。
脏读 | 不可重复度 | 幻读 | |
---|---|---|---|
读未提交 | 有 | 有 | 有 |
读已提交 | 无 | 有 | 有 |
可重复度[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 {
@Bean
public 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;
}
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public 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
<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("测试日志");
}
}