这里写目录标题
IOC
IOC: 控制反转——把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。
控制: 创建对象,对象的属性赋值,对象之间的关系管理。
反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,给属性赋值。
为什么要使用 ioc : 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。
java中创建对象有哪些方式:
- 构造方法 , new Student()
- 反射
- 序列化
- 克隆
- ioc :容器创建对象
- 动态代理
xml
xml:可扩展标记语言
步骤01:调用xml文件
ApplicationContext appc=new ClassPathXmlApplicationContext(config);
ApplicationContext表示Spring对象
步骤02:
通过spring对象 appc 获取相关容器中对象:
给对象赋值
01参数赋值:
顾名思义:直接给参数赋值
02引用赋值:
即A类中的一个参数 是 另外一个类 B
所以在给B赋值时,需要先将B赋值,在进行引用赋值
<name="" ref="">
03构造方法赋值
构造方法赋值 一个ca表示构造方法的一个参数
name:形参名
index:构造方法的参数位置 从左往右0,1,2,的顺序
value:构造方法的形参类型是简单类型,使用value
ref:构造方法的形参类型是引用类型用ref
也可以将name换成index 表示构造方法中参数的位置
04按名称注入
用autowire=“byName” 对引用对象赋值 id名要与类中该对象名一致
<bean id="myStudent" class="com.wish.ba02.Student" autowire="byName">
<property name="name" value="zhangsan"/>
<property name="age" value="20"/><!--根据set属性 来赋值 有Set方法就可以从这赋值-->
<!--
<property name="school" ref="mySchool"/>
-->
</bean>
<bean id="myStudent" class="com.wish.ba01.Student">
<property name="name" value="zhangsan"/>
<property name="age" value="20"/><!--根据set属性 来赋值 有Set方法就可以从这赋值-->
</bean>
05按名称注入
java类中引用类型的数据类型和spring容器中(配置文件) 的class属 性
是同源关系的, 这样的bean能够赋值给引用类型
byType同源就是一类的意思:
1:java类中引用类型的数据类型和spring容器中(配置文件) 的class属 性
是同源关系的, 这样的bean能够赋值给引用类型
2:java类中引用类型的数据类型和bean的class的值父子类关系的。
3:java类中引用类型的数据类型和bean的class的值接口和实现类关系的
多配制文件
<!--
表示主配置文件:包含其他配置文件
语法:<import resource=""
关键字classpath 其他文件的路径(class文件所在目录)
-->
<!--
<import resource="classpath:ba03/applicationStudent.xml"/>
<import resource="classpath:ba03/applicationSchool.xml"/>
-->
<import resource="classpath:ba03/applicationS*.xml"/>
组件扫描器
<!--组件扫描器context:component-scan 就是java对象的意思
base-package 指定注解在你项目中的包名
工作方式:
扫描base-package中所有的类 并找到类的注解 按照注解的功能创建(通过无参构造方法)对象,给属性赋值。
-->
<context:component-scan base-package="com.hdu.ba03"/>
通过在类中加注解 赋值 修改起来麻烦 不建议使用
AOP
面向切面编程
动态代理 做了一个 标准化。
Spring 内部实现了AOP规范
Spring 主要在事务处理时使用AOP
项目中 很少使用Spring中的AOP实现 因为其 比较笨重
aspectJ
aspectJ实现原理
<aop:aspectj-autoproxy />的proxy-target-class属性值和是否有申明接口决定了代理的方式:
1.如果不写,默认为false,表示使用jdk动态代理织入增强;
2.显示的配置成true,<aop:aspectj-autoproxy poxy-target-class=“true”/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
一般使用aspectJ :一个专门做AOP的框架
Spring 框架集成了aspectJ框架,所以 可以通过Spring使用aspectJ的功能
aspectJ框架实现AOP的两种方式
使用xml的配置文件
配置全局事务
切面执行时间
也可以使用xml中的标签来表示执行的时间
aspectJ实现步骤
01加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.17.RELEASE</version>
</dependency>
注:这里添加的时候老是爆红,无论是刷新还是下载相对应依赖(在 idea里操作)都无效,然后将5.2.5改成5.2.17就好了 .17也是它自己提示才得到
02创建目标类接口
public interface SomeService {
void dosome(String name,Integer age);
}
03实现目标类
public class SomeServiceImpl implements SomeService {
@Override
public void dosome(String name,Integer age) {
System.out.println("******执行dosome******");
}
}
04创建一个切面类,切面类可以表示获取时间或日志等增强操作
@Aspect在方法上面表示注解类
@Before 在方法名上面表示前置通知注解
特点:在目标方法执行之前执行 不会改变目标方法的执行结果 不影响目标方法
@Aspect
public class Myaspect01 {
@Before(value = "execution(void com.hdu.ba01.SomeServiceImpl.dosome(String,Integer))")//位置+时间
public void myBefore(){
System.out.println("1前置通知 切面功能:在目标方法之前 输出执行时间:"+new Date());
}
@Before(value = "execution(void *..SomeServiceImpl.do*(String,Integer))")//位置+时间
public void myBefore2(){
System.out.println("2前置通知 切面功能:在目标方法之前 输出执行时间:"+new Date());
}
@Before(value = "execution(void *..SomeServiceImpl.do*(..))")//位置+时间
public void myBefore3(){
System.out.println("3前置通知 切面功能:在目标方法之前 输出执行时间:"+new Date());
}
}
//public 可省略 包名可以用 *..表示
//方法名后面可以用(..)代替(String,Integer)
//甚至方法名那 都可以以某个开始的字符 来替换dosome --->do*
//通知可以执行多个 如果参数 那写错了 或者少写 就加不进去
具体如上面表示。
05xml配置文件
<bean id="SomeServiceImpl" class="com.hdu.ba01.SomeServiceImpl"/>
<bean id="myAspect" class="com.hdu.ba01.Myaspect01" />
06测试类
package com.hdu;
import com.hdu.ba01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest01 {
@Test
public void test01(){
String config="applicationContext.xml";
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService someService=(SomeService) applicationContext.getBean("SomeServiceImpl");
//执行目标方法
someService.dosome("lisi",24);
}
}
AOP作用
1在目标类不修改的源代码的情况下 增加功能
2减少重复代码
3专注业务功能的实现
4解耦合:业务功能和日志,事务这些非业务功能点额 耦合。
如何给指定多个目标类添加功能
@Before(valuess="execution(*com.hdu.ba01.*ServiceImp.*(..)")
这样指定某个包下的 ServiceImp 所有方法都可已添加功能。
JoinPoint业务方法
切面功能要用到目标类方法的信息,就 加入JoinPoint
@Before(value = "execution(void com.hdu.ba01.SomeServiceImpl.dosome(String,Integer))")//位置+时间
public void myBefore(JoinPoint jp){
//获取方法的完整定义:
System.out.println("方法的签名(定义)"+jp.getSignature());
System.out.println("方法名"+jp.getSignature().getName());
//System.out.println(""+jp.getSignature().getModifiers());
System.out.println("获取方法的定义1前置通知 切面功能:在目标方法之前 输出执行时间:"+new Date());
}
后置通知AfterReturning
后置通知对目标方法返回值进行修改,并不会改变目标方法的返回值,因为这里的参数传递是值传递
可以对目标方法的返回值进行后续操作。
环绕通知
有固定参数ProceedingJoinPoint 能修改目标方法返回结果,影响最后结果
ProceedingJoinPoint继承于JoinPoint
定义格式
特点
结果
那么如何根据目标方法的返回值来控制目标方法
ProceedingJoinPoint pip
{ String name="";
Object args[]=pip.getArgs();//获取参数
if(args!=null&&args.length>=1){//说明有参数
Object arg=args[0]
name=(String)arg;
}
//然后根据参数 来判断 目标方法中的传参是否符合要求
if("zhangsan".equals(name)){
result=pip.proceed();
}
}
ProceedingJoinPoint继承于JoinPoint,其可以获取目标方法参数
然后在环绕通知内部 定义一个已知参数 与目标参数进行比较 从而来控制目标方法的进行
修改目标方法的结果,会影响最后的结果
为什么呢? 因为后面目标方法的调用被换成了环绕通知的的类,也就返回环绕通知的结果
在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法. 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行.
即上面pip.proceed()方法会被用来执行被代理的方法,所以此时的result就是目标方法的返回值,而在环绕通知中,可以通过修改这个返回值来达到修改目标方法的返回值
异常通知
最终通知After
PointCut
给目标类起别名
Spring集成MyBatis
回顾用Mybatis访问数据库
01——pom.xml加入mybatis配置文件
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<scope>runtime</scope>
</dependency>
<build>
<!--把src目录下的xml文件拷贝到targer文件夹下-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
02建立一个对象类
03——建立Dao接口
public interface StudentDao {
List<Student> selectStudent();
}
04——Dao包下的接口配置文件,用来写sql语句
05——mybatis配置文件,用来连接数据库的配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/wishdatabase"/>
<property name="username" value="root"/>
<property name="password" value="mysql.123"/>
</dataSource>
</environment>
</environments>
<!--映射文件 包含相关sql语句-->
<mappers>
<mapper resource="com/hau/dao/StudentDao.xml"/>
<!-- <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
</mappers>
</configuration>
<!--
主配置文件 数据库的配置信息 sql映射文件的位置
-->
06访问数据库执行sql语句
使用mybatis的动态代理,使用sqlSession.getMapper(dao接口)
这样就不用去写一个接口实现类,即不用每次都new一个对象 我们只需修改sql语句
package com.hau.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* @author #Description MybatisUtil
* #Date: 2021/9/16 14:37
*/
public class MybatisUtil {
private static SqlSessionFactory factory=null;
static {
String config="mybatis.xml";
try {
InputStream in= Resources.getResourceAsStream(config);
factory=new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSesion(){
SqlSession sqlSession=null;
if (factory!=null){
sqlSession=factory.openSession();
}
return sqlSession;
}
}
写一个Test来进行测试,调用mybatis的动态代理
package com.hau;
import com.hau.dao.StudentDao;
import com.hau.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class TestMybtis {
@Test
public void testSelectStudent(){
SqlSession sqlSession= MybatisUtil.getSqlSesion();
StudentDao dao=sqlSession.getMapper(StudentDao.class);
dao.selectStudent();
}
}
Spring集成MyBatis步骤
Spring和mybatis的集成步骤;
1.新建maven项目
2.加入maven的依赖
1)spring依赖
2)mybatis依赖
3)mysql驱动
4)spring的事物依赖
5)mybatis和spring的集成依赖:mybatis官方提供的,用来在spring项目中创建mybatis的SqlSessionFactory ,dao对象
3.创建实体类
4.创建dao接口和mapper文件
5.创建mybatis主配置文件
6.创建Service接口和实现类,属性是dao
7.创建spring的配置文件 :声明mybatis的对戏交给spring创建
1)数据源DataSource——durid
2)SQLSessionFactory
3)dao对象
4)声明自定义的service
8.创建测试类 完成数据库的访问
加入各种依赖
<dependencies>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--spring核心ioc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<scope>runtime</scope>
</dependency>
<!-- 数据库连接池 alibaba 的 druid 用来替代mybatis的连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.7</version>
</dependency>
</dependencies>
build部分
<build>
<resources>
<!--解决mybatis-generator-maven-plugin运行时没有将XxxMapper.xml文件放入target文件夹的问题-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/ *.xml</include>
</includes>
</resource>
<!--解决mybatis-generator-maven-plugin运行时没有将jdbc.properites文件放入target文件夹的问题-->
</resources>
<!--指定jdk的版本-->
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
创建各种类和接口
创建实体类
创建dao接口和mapper文件
public interface UserDao {
int insertUser(User user);
List<User> selectUser();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hdu.dao.UserDao">
<insert id="insertUser">
insert into user values(#{userid},#{username},#{password},#{sex},#{email})
</insert>
<select id="selectUser" resultType="com.hdu.domain.User"><!--selectuser-->
select userid,username,password,sex,email from user order by userid desc
</select>
</mapper>
<!--
select
update
insert
delete
resultType 结果类型
-->
创建mybatis主配置文件
以下文件中连接数据库部分交给spring配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
-->
<!--用spring集合mybatis之后 可以简化很多-->
<!--
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/wishdatabase"/>
<property name="username" value="root"/>
<property name="password" value="mysql.123"/>
</dataSource>
</environment>
</environments>
-->
<!--设置别名-->
<typeAliases>
<!--实体类所在的包名-->
<package name="com.hdu.domain"/>
</typeAliases>
<mappers>
<package name="com.hdu.dao"/>
<!-- <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
</mappers>
</configuration>
<!--
主配置文件 数据库的配置信息 sql映射文件的位置
-->
创建Service接口和实现类
public interface UserService {
int adduser(User user);
List<User> queryUser();
}
package com.hdu.service.impl;
import com.hdu.dao.UserDao;
import com.hdu.domain.User;
import com.hdu.service.UserService;
import java.util.List;
/**
* @author #Description UserServiceImpl
* #Date: 2021/10/15 21:13
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
//使用set注入来赋值
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public int adduser(User user) {
int nums=userDao.insertUser(user);
return nums;
}
@Override
public List<User> queryUser() {
List<User> users=userDao.selectUser();
return users;
}
}
创建Spring的配置文件
重点部分
创建数据池——duird
连接数据库 代替Mybatis中的功能
声明mybatis中的SQLSessionFactoryBean类,其可以创建SqlSessionFactory
创建dao对象01——读取mybatis的主配置文件——其可以访问sql相关的接口类和实体类
创建dao对象02——通过Service去访问dao
<?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">
<!--声明数据源 DataSource 连接数据库 代替Mybatis中的功能-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--set注入 给DruidDataSource连接数据库 connection连接对象-->
<!--<property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!--可以通过property写配置信息 <context:property-placeholder location="classpath:配置信息">-->
<property name="url" value="jdbc:mysql://localhost:3306/wishdatabase"/>
<property name="username" value="root"/>
<property name="password" value="mysql.123"/>
<property name="maxActive" value="20"/><!--最多能有多少个连接同时存在-->
</bean>
<!--声明mybatis中的SQLSessionFactoryBean类 SQLSessionFactoryBean内部创建SQLSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入 把数据库连接池赋给了dataSource 连接池-->
<property name="dataSource" ref="myDataSource"/>
<!--读取mybatis的主配置文件位置
configLocation属性是Resource类型 读取配置文件
它的赋值,使用value,指定文件的路径,使用classpath表示文件位置-->
<property name="configLocation" value="classpath:mybartis.xml"/><!--其他文件路径要加classpath-->
</bean>
<!--创建dao对象,使用SqlSeession的getmapper 在内部调用getMapper()生成每个dao接口的代理对象。-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名,包名是dao接口的所在的包名
扫描这个包中所有的接口 把每个接口都执行一次getMapper()方法 得到每个接口的dao对象
把创建好的dao对象放入到spring容器中-->
<property name="basePackage" value="com.hdu.dao"/>
</bean>
<!--声明service 通过Service去访问dao userService userDao-->
<bean id="UserService" class="com.hdu.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
测试类
package com.hdu;
import com.hdu.dao.UserDao;
import com.hdu.domain.User;
import com.hdu.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
/**
* @author #Description MyTest
* #Date: 2021/10/16 14:08
*/
public class MyTest {
@Test
public void Test(){
String config="applicationcontext.xml";
ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
String[] name=ctx.getBeanDefinitionNames();
for (String na:name
) {
System.out.println("容器名称"+na+"*"+ctx.getBean(na));
}
}
@Test
public void Test02(){
String config="applicationcontext.xml";
ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
UserDao dao=(UserDao) ctx.getBean("userDao");
User user=new User();
user.setUserid(160);//主键不能重复
user.setUsername("张三");
user.setPassword("2020");
user.setSex("男");
user.setEmail("187182@");
int nums=dao.insertUser(user);
System.out.println(nums);
List<User> users=dao.selectUser();
for (User u:users
) {
System.out.println(u);
}
}
@Test
public void TestService(){
String config="applicationcontext.xml";
ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
UserService service=(UserService) ctx.getBean("UserService");
User user=new User();
user.setUserid(200);//主键不能重复
user.setUsername("张三");
user.setPassword("2020");
user.setSex("男");
user.setEmail("187182@");
int nums=service.adduser(user);
//spring 和mybatis 事务自动提交 无需执行SqlSess.commit()
System.out.println(nums);
}
@Test
public void TestServiceSelect(){
String config="applicationcontext.xml";
ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
UserService service=(UserService) ctx.getBean("UserService");
List<User> list=service.queryUser();
for (User u:list
) {
System.out.println(u);
}
}
}
Spring开启事务
Spring进行事务管理的常用API
Spring进行事务管理的常用API
声明事务管理器如:
1)PlatformTransactionManager:平台事务管理器
Spring进行事务操作时候,主要使用一个PlatformTransactionManager接口,它表示事务管理器,即真正管理事务的对象。
Spring并不直接管理事务,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,也就是将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring针对不同的持久化框架,提供了不同PlatformTransactionManager接口的实现类:
org.springframework.jdbc.datasource.DataSourceTransactionManager :使用 Spring JDBC或MyBatis 进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager :使用 Hibernate版本进行持久化数据时使用
2)Spring的这组接口是如何进行事务管理的
平台事务管理器根据事务定义的信息进行事务的管理,事务管理的过程中产生一些状态,将这些状态记录到TrancactionStatus里面。
3)TrancactionStatus:事务的状态
在上面 PlatformTransactionManager 接口有一个方法getTransaction(),这个方法返回的是 TransactionStatus对象,然后程序根据返回的对象来获取事务状态,然后进行相应的操作。
而 TransactionStatus 这个接口的内容如下:
这个接口描述的是一些处理事务提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候需要应用对应的事务状态。
aspectj配置aop