SpringFarmwork
一、什么是IOC
首先要认识到IOC是一种思想,简单来说,就是将我们的手中的控制权转交出去,就像是玩斗地主时候的托管。在SpringFarmwork中,主要的表现就是,我们将我们手中的创建对象的权利转交了出去,我们需要对象的时候只需要向计算机去讨要就行了。
二、什么是DI
DI有可以叫做依赖注入,DI是IOC的一种实现方式。在SpringFarmwork中我们最主的方式有通过Set方式注入和通过Constructor方式注入。通俗的说就是给我们创建的对象的属性赋值,可以通过对象的Set方法和构造器来实现。有了这两个概念以后,我们就可以来尝试简单使用一下SpringFarmwork。
三、简单使用SpringFarmwork
首先我们创立一个简单的Maven项目,导入相关的jar包<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<!--导入这一个jar包其他相关的也会跟着导入进来-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.14</version>
</dependency>
之后我们来写Spring的配置文件,把它放在resources文件夹下
<?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">
</beans>
假如说我们有一个简单的实体类User(实现了Get和Set方法,重写了toString())
package com.nbu.pojo;
public class User {
private String username;
private String password;
public User() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
我们现在来尝试着来注入一下它
<?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">
<!--这里是通过Set方法注入的-->
<bean id="user" class="com.nbu.pojo.User">
<property name="username" value="root"/>
<property name="password" value="123456789"/>
</bean>
</beans>
这样其实就算是注入完毕了,其中这个对象的username属性值为root,password属性值为123456789,接下来我们尝试着去获取这个对象。
package com.nbu;
import com.nbu.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//这里是先去把我们前面配置的xml文件注册
ApplicationContext Context =new ClassPathXmlApplicationContext("springDao.xml");
//通过上下文来获取我们前面在xml文件里写入的user
User user = Context.getBean("user",User.class);
//打印这个user的内容
System.out.println(user);
}
}
运行的结果如下
这里我们可以看到结果是正确的,我们接下来尝试着使用一下构造器来完成属性注入,不过这样的话,我们就需要来修改一下我们的User类了,因为我们之前的User类里是无参构造函数。
User类修改完毕后如下
package com.nbu.pojo;
public class User {
private String username;
private String password;
public User() {
}
public User(String username,String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
我们修改一下我们之前的Spring的配置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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通过构造器注入的方法有三种这里是比较舒服的一种-->
<!--下面三种在配置文件里放任意一个就行了不然会报错-->
<!--这里我们是通过构造器的参数的参数名来实现的属性注入-->
<bean id="user" class="com.nbu.pojo.User">
<constructor-arg name="username" value="root"/>
<constructor-arg name="password" value="123456789"/>
</bean>
<!--第二种方法我们来尝试通过参数的下标来完成属性注入-->
<bean id="user" class="com.nbu.pojo.User">
<constructor-arg index="0" value="root"/>
<constructor-arg index="1" value="123456789"/>
</bean>
<!--第三种方式是通过构造器中的参数的数据类型来完成属性注入-->
<!--虽然这里两个都是String所以是按顺序完成了属性的注入-->
<bean id="user" class="com.nbu.pojo.User">
<constructor-arg type="java.lang.String" value="root"/>
<constructor-arg type="java.lang.String" value="123456789"/>
</bean>
</beans>
运行的结果如下
实际如果只是使用就是这么的简单,没有多么的复杂,整个SpringFarmwork就一个IOC和一个AOP就没了,重要的是将他和其他的框架整合在一起(后面我会介绍将他和Mybatis整合在一起使用)
让我们接着往下看,我们现在来一起看自动注入这个是比较重要的。
四、自动注入
所谓spring自动注入,是指容器中的一个组件中需要用到另一个组件(例如聚合关系)时,依靠spring容器创建对象,而不是手动创建。(ps:这句话引用自https://www.cnblogs.com/dubhlinn/p/10708142.html)。由于我们之前的目录结构里,没有这中聚合关系,所以我们这里新建两个实体类Student和Address
package com.nbu.pojo;
public class Student {
private String name;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address adress) {
this.address = adress;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", " + address.toString() +
'}';
}
}
package com.nbu.pojo;
public class Address {
private String country;
private String province;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
@Override
public String toString() {
return "Address={" +
"country='" + country + '\'' +
", province='" + province + '\'' +
'}';
}
}
我们现在来使用第一种方式byName注入,就是我们这里可以通过Student当中Address的属性名和已经注册了的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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.nbu.pojo.Address">
<property name="country" value="中国"/>
<property name="province" value="福建省"/>
</bean>
<!--我这里并没有去收动的去配置属性addres-->
<!--如果要手动配置的话如下
<bean id="student" class="com.nbu.pojo.Student">
<property name="name" value="阿昌"/>
<property name="address" ref="address"/>
</bean>
-->
<bean id="student" class="com.nbu.pojo.Student" autowire="byName">
<property name="name" value="阿昌"/>
</bean>
</beans>
运行结果如下
这就完成了通过属性名完成自动注入,我们接下来使用第二种方式byType完成属性注入
<?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="address" class="com.nbu.pojo.Address">
<property name="country" value="中国"/>
<property name="province" value="福建省"/>
</bean>
<!--这里修改一下autowire后面的值就行了-->
<bean id="student" class="com.nbu.pojo.Student" autowire="byType">
<property name="name" value="阿昌"/>
</bean>
</beans>
感觉是不是很简单,这里我要说一下注意点,就是byType必须保证找到的结果是唯一的,不然就会报错。
五、代理模式
为什么要说这个呢?那是因为AOP的原理实际上就是代理模式,理解代理模式后对于AOP的理解会更加的容易。代理模式主要分为两种,一种是静态代理,一种是动态代理,不过其本质都是在不改变原来已有的代码的情况下实现方法的增强。
我们先来看静态代理,首先我们来做一些准备,建立一个原本实际的Real类和代理Proxy类和一个接口CanDo表示能做些什么。
分别如下:
public interface CanDo {
public void Do();
}
public class Real implements CanDo{
public void Do(){
System.out.println("Real 做了一些事情");
}
}
public class Proxy implements CanDo{
private CanDo real;
public void SetPro(CanDo real)
{
this.real = real;
}
public void Do()
{
if (real!= null)
{
System.out.println("前置的增强");
real.Do();
System.out.println("后置的增强");
}
}
}
测试代码和运行结果如下:
public class Test {
public static void main(String[] args) {
Real real = new Real();
Proxy proxy = new Proxy();
proxy.SetPro(real);
proxy.Do();
}
}
这种方式就是静态代理,动态代理的话,是通过Java的反射来做的,和静态代理的实现是差不多的,接下来我们就在静态代理基础上做些改动来实现动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Proxy implements InvocationHandler{
private CanDo real;
public void SetProxyObject(CanDo real)
{
this.real =real;
}
public Object GetPro()
{
return java.lang.reflect.Proxy.newProxyInstance(this.getClass().getClassLoader(),real.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
method.invoke(real,args);
System.out.println("后置增强");
return null;
}
}
public class Test {
public static void main(String[] args) {
CanDo real = new Real();
Proxy proxy = new Proxy();
proxy.SetProxyObject(real);
CanDo canDo = (CanDo) proxy.GetPro();
canDo.Do();
}
}
输出结果
了解了代理模式后,我们就可以来学习AOP了。
六、什么是AOP
AOP又叫做面向切面编程,我们可以借助上面的代理模式来理解这一概念。 我们先来理解几个概念(以下内容引用自狂神说)
切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
切入点(PointCut):切面通知 执行的 “地点”的定义。
连接点(JointPoint):与切入点匹配的执行点。
我们接下来就使用尝试一下在Spring当中使用AOP。使用AOP之前,我们先要导入一些jar包,以及一些AOP的约束
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
这里我们使用Spring的API来实现,当然可以自定义,之后会演示。我们现在先写两个切面Before和After以及一个目标Temp
package com.nbu.Annocation;
import org.aopalliance.aop.Advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Before implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知");
}
}
package com.nbu.Annocation;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class After implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置通知");
}
}
package com.nbu.Dao;
public class Temp {
public void Test(){
System.out.println("连接点。。。");
}
}
当然写完后要注册Bean,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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="user" class="com.nbu.pojo.User">
<property name="username" value="root"/>
<property name="password" value="123456789"/>
</bean>
<bean id="address" class="com.nbu.pojo.Address">
<property name="country" value="中国"/>
<property name="province" value="福建省"/>
</bean>
<bean id="student" class="com.nbu.pojo.Student" autowire="byType">
<property name="name" value="阿昌"/>
</bean>
<bean id="temp" class="com.nbu.Dao.Temp"/>
<bean id="before" class="com.nbu.Annocation.Before"/>
<bean id="after" class="com.nbu.Annocation.After"/>
<aop:config>
<aop:pointcut id="testPointCut" expression="execution(* com.nbu.Dao.Temp.*(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="testPointCut"/>
<aop:advisor advice-ref="after" pointcut-ref="testPointCut"/>
</aop:config>
</beans>
我们测试一下
package com.nbu;
import com.nbu.Dao.Temp;
import com.nbu.pojo.Student;
import com.nbu.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext Context =new ClassPathXmlApplicationContext("springDao.xml");
Temp temp = Context.getBean("temp", Temp.class);
temp.Test();
}
}
结果如下
如果是要自定以的话,我们就要去写一个类这个类不用继承任何的接口
public class TestAop{
public void Before(){
System.out,println("前置通知");
}
public void After(){
System.out.println("后置通知");
}
}
同时,xml文件里的配置要变为
<bean id="testApp" class"com.nbu.TestApp">
<aop:config>
<aop:aspect ref="testApp">
<aop:pointcut id="testPointCut" expression="execution(* com.nbu.Dao.Temp.*(..))"/>
<aop:advisor method="Before" pointcut-ref="testPointCut"/>
<aop:advisor method="After" pointcut-ref="testPointCut"/>
</aop:aspect>
</aop:config>
结果和上面是一样的。
七、整合Mybatis框架
如果你不知道Mybatis的话可以去看一下我之前写的,做一个简单的了解https://blog.csdn.net/qq_52239796/article/details/122650482?spm=1001.2014.3001.5501
我们接下来,来接着介绍。在开始之前,我们依旧要做一些准备,先导入一个jar包。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
当然对于这个jar包的导入版本,和你其他jar包导入的版本是有关联的
我们还要导入数据库相关的jar包,还有连接需要的jar包
<!-- pool -->
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<!-- dbcp依赖包 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!--dbcp2依赖包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
这样我们的环境就搭建好了,接下来,我就就在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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1/stu"/>
<property name="username" value="输入用户名"/>
<property name="password" value="输入密码"/>
</bean>
<!--建立SqlSession工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/nbu/Dao/*.xml"/>
</bean>
<!--得到SqlSessionTemplate,就是SqlSession-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
</bean>
<bean id="user" class="com.nbu.pojo.User">
<property name="username" value="root"/>
<property name="password" value="123456789"/>
</bean>
<bean id="address" class="com.nbu.pojo.Address">
<property name="country" value="中国"/>
<property name="province" value="福建省"/>
</bean>
<bean id="student" class="com.nbu.pojo.Student" autowire="byType">
<property name="name" value="阿昌"/>
</bean>
<bean id="temp" class="com.nbu.Dao.Temp"/>
<bean id="before" class="com.nbu.Annocation.Before"/>
<bean id="after" class="com.nbu.Annocation.After"/>
<aop:config>
<aop:pointcut id="testPointCut" expression="execution(* com.nbu.Dao.Temp.*(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="testPointCut"/>
<aop:advisor advice-ref="after" pointcut-ref="testPointCut"/>
</aop:config>
</beans>
接下来的操作就和使用Mybatis差不多了,Spring中的配置其实就是代替了mybatis-config.xml,不过我们还是保留mybatis-config.xml来进行一些别名、插件、设置之类的配置。
我们从Spring中去请求SqlSessionTemplate然后通过其获得Mapper,再通过Mapper获得接口,通过接口去使用自己在Mapper中实现的SQL语句就行了。这里就不演示了。