首先逛一下spring官网:Spring | Home
为什么用框架,以及spring框架的优点就不再敖述了,正如官网所言“Spring makes Java simple”,废话不多说直接开始吧!
先简单说一下二者的基本思想:
1.IOC-Inversion of Control(控制反转)
是一种设计思想,DI是具体实现方式(DI在后面代码中解释),其大体思想就是:
- 创建项目配置文件(xml)
- 通过定义bean标签(写入id和对应的class),告诉Spring容器这些类需要管理(这里还可以通过注解实现)
- 容器通过反射机制创建对象保存到一个集合Map中
- 开发者通过Bean工厂或者上下文对象通过传入配置文件中的id获取对应的类对象
这样就把对象的创建和销毁交给了容器,开发者不在自己创建,之间获取使用即可。
2.AOP-Aspect Oriented Programming(面向切面编程)
通过预编译方式和运行期动态代理实现程序功能的统一维护的技术。其目的就是各自做自己擅长的事儿,专注于核心业务,提高代码的利用率,通过代理的方式实现。
比如一个方法中包括了核心业务,同时又要添加日志、异常处理等,使代码繁琐,使用AOP将其分开,日志用专门的方法,异常处理也有自己的类,核心业务只关心具体的业务不考虑其他。
实现切面主要有静态代理和动态代理,其中静态代理其实就是把核心业务和日志等其他事务分开,核心业务提取一个接口,具体的核心业务层实现具体功能(被代理对象),同时增加一个代理类(实现上述接口),代理类中添加接口对象和切面对象,在重写相关方法的同时分别调用被代理类和切面类实现重组,这样核心业务就不用管理那些其他事务,而且当其他事务发生变化时核心业务是不受影响的,只需调整切面类即可。(静态代理详细介绍可参考Spring- AOP详解之静态代理_冷丁_的博客-CSDN博客_springaop静态代理)
动态代理实现主要有jdk动态代理和cglib动态代理,前者要求被代理对象必须实现接口(通过Proxy.newProxyInstance()完成),当需要功能扩展又没有实现接口时可使用cglib(Enhancer.create()完成)。(可参考【Spring】JDK、CGLIB动态代理与AOP_智商三岁半i的博客-CSDN博客_aop cglib jdk)
3.SpringJDBC:
spring整合jdbc的时候我们都是直接让我的dao继承Spring提供的JdbcDaoSupport类,该类中提供了JdbcTemplate模板可用。
接下来用一个简单的例子具体说明:
首先,创建maven项目再pom.xml中添加依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lxj</groupId>
<artifactId>MySpring03</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建一个学生类:
package com.lxj.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Students {
//需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上
private Integer id;
@Value("小明")
private String name;
@Value("21")
private Integer age;
public Students() {
System.out.println("Students---Students无参构造");
}
public Students(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
System.out.println("Students---Students全参构造");
}
public void init(){
System.out.println("Student 初始化方法");
}
public void destroy(){
System.out.println("Student 销毁方法");
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
然后做一个dao层(注意这里没有用注解,因为spring整合jdbc的配置通过xml进行配置注入):
package com.lxj.dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
// 在类上添加注解@Component表示该类创建对象的权限交给Spring容器。注解的value属性用于指定bean的id值,value可以省略。
// @Component 不指定 value 属性,bean 的 id 是类名的首字母小写
// 另外spring提供了另外3个注解:
// @Repository : 用于dao实现类的的注解
// @Service: 用户service实现类的注解
// @Controller: 用于controller实现类的注解
// 这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义,@Service创建业务层对象,业务层对象可以加入事务功能,@Controller 注解创建的对象可以作为处理器接收用户的请求。
//@Component(value="stuDao")
//@Repository(value="studentDao1")
public class StudentDao extends JdbcDaoSupport {
public StudentDao() {
System.out.println("StuDao 无参构造");
}
//spring整合jdbc的时候我们都是直接让我的dao继承Spring提供的JdbcDaoSupport类,该类中提供了JdbcTemplate模板可用。
public int add(Integer id, String name, Integer age){
String sql="insert into student(id,name,age) value (?,?,?)";
return this.getJdbcTemplate().update(sql, id,name,age);
}
}
再搞一个Service层:
package com.lxj.service;
public interface SService {
int add(int id,String name,int age);
}
package com.lxj.service;
import com.lxj.dao.StudentDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service(value="stuService")
public class StuService implements SService {
// Spring提供了对 jdk中@Resource注解的支持。
// @Resource 注解既可以按名称匹配Bean,也可以按类型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。
// @Resource 可在属性上,也可在 set 方法上。
// @Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入。
// @Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id
//@Autowired(required = false)
@Resource(name="stuDao")
private StudentDao stuDao;
public StuService() {
System.out.println("StuService 无参构造");
}
public StuService(StudentDao stuDao) {
System.out.println("StuService 有参构造");
this.stuDao = stuDao;
}
@Override
public int add(int id, String name,int age) {
System.out.println("Service add start...");
int add = stuDao.add(id, name, age);
return add;
}
public StudentDao getStuDao() {
return stuDao;
}
public void setStuDao(StudentDao stuDao) {
this.stuDao = stuDao;
}
}
搞一个切面类:
package com.lxj.asp;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component //将切面对象交给spring容器
@Aspect //框架注解 标识当前类是一个切面类
public class MyAspect {
/**
* 当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。
* AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。
* 其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均
* 可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。
* 这个使用@Pointcut 注解方法一般使用 private 的标识方法,即没有实际作用的方法。
*/
@Pointcut("execution(* com.lxj..*.add*(..))")
private void pointCut1(){}
/**
* 声明前置通知
* @param jp
*/
@Before("pointCut1()")
public void before(JoinPoint jp){
System.out.println("前置消息:");
String name=jp.getSignature().getName();
Object[] args= jp.getArgs();
System.out.println("拦截的方法是:"+name+",参数格式为:"+args.length);
System.out.println("参数列表为:");
for (Object arg:args) {
System.out.println("\t"+arg);
}
}
/**
* AfterReturning 注解声明后置通知
* value: 表示切入点表达式
* returning 属性表示 返回的结果,如果需要的话可以在后置通知的方法中修改结果
*/
@AfterReturning(value = "pointCut1()",returning = "result")
public Object afterReturn(Object result){
if (result!=null){
int res=(int) result;
if (res==0){
result=false;
}
}
System.out.println("后置通知:"+result);
return result;
}
/**
* Around 注解声明环绕通知
* ProceedingJoinPoint 中的proceed方法表示目标方法被执行
*/
@Around(value = "pointCut1()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕方法———目标方法执行前");
Object proceed=pjp.proceed();
System.out.println("环绕方法---目标方法执行后");
return proceed;
}
/**
* AfterThrowing 注解声明异常通知方法
* value: 表示切入点表达式
* returning 属性表示 返回的结果,如果需要的话可以在后置通知的方法中修改结果
*/
@AfterThrowing(value = "pointCut1()",throwing = "ex")
public void exception(JoinPoint jp,Throwable ex){
System.out.println("异常通知:在目标方法执行出现异常时调用此通知,否则不执行");
System.out.println(jp.getSignature()+"方法异常:"+ex.getMessage());
}
/**
* After 注解声明为最终通知
*/
@After("pointCut1()")
public void myFinally(){
System.out.println("最终通知:方法结束");
}
}
resources下建一个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: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标签也可以用一个,多个值用逗号隔开;
也可以直接指定到父包,自动扫描父包下的子包-->
<context:component-scan base-package="com.lxj.bean,com.lxj.asp,com.lxj.dao,com.lxj.service"></context:component-scan>
<!--在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。
这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动
扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理-->
<!--开启注解AOP的使用-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--spring的配置文件application.xml中需要创建数据源和给TeamDao中的jdbcTemplate赋值-->
<!--创建数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--创建JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="stuDao" class="com.lxj.dao.StudentDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property><!--这里指定的StuDao类需要继承自JdbcDaoSupport-->
</bean>
<!--补充:xml配置文件-->
<!--
bean标签的属性:
id="自定义的对象名称" ,要求唯一
name="bean对于的一个标识“,一般使用id居多
class="类的完全限定名"
scope="singleton/prototype" 单例/多例
singleton:默认值,单例:在容器启动的时候就已经创建了对象,而且整个容器只有为一个的一个对象
prototype:多例,在使用对象的时候才创建对象,每次使用都创建新的对象
lazy-init="true/false" 是否延迟创建对象,只针对单例有效
true:不延迟创建对象,容器加载的时候立即创建
false:默认加载,使用对象的时候才去创建对象
init-method="创建对象之后执行的初始化方法"
destroy-method="对象销毁方法,调用容器destroy方法的时候执行"
-->
<!--1、默认构造-->
<!--bean id="stuDents2" class="com.lxj.bean.Students" scope="singleton" lazy-init="true" init-method="init" destroy-method="destroy"/-->
<!-- 2、通过带参数的构造方法-->
<!--bean id="stuDents3" class="com.lxj.bean.Students"-->
<!--name:表示参数的名称-->
<!--constructor-arg name="id" value="8"/>
<constructor-arg name="name" value="小华"/>
<constructor-arg name="age" value="18"/>
</bean-->
<!--bean id="stuDents4" class="com.lxj.bean.Students"-->
<!--index:表示参数的下标索引-->
<!--constructor-arg index="0" value="7"/>
<constructor-arg index="1" value="小鹏"/>
<constructor-arg index="2" value="16"/>
</bean-->
<!--3、通过工厂方法:3.1 静态方法 Team team1 = MyFactory.staticFun();-->
<!--bean id="staticTeam" class="com.lxj.MyFactory" factorymethod="staticFun"></bean-->
<!--3、通过工厂方法:3.2 实例方法 MyFactory factory=new MyFactory();Team team = factory.instanceFun();-->
<!--bean id="factory" class="com.lxj.MyFactory"></bean>
<bean id="instanceTeam" factory-bean="factory" factory-method="instanceFun">
</bean-->
<!--
在代码中调用:
String springConfig="application.xml";
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext(springConfig);
Students stu2 = (Students) applicationContext.getBean("stuDents2");
applicationContext.close();//关闭容器
-->
<!--补充:xml注入-->
<!--bean id="stuDao" class="com.lxj.dao.StuDao"></bean-->
<!--bean id="stuService" class="com.lxj.StuService"-->
<!--使用set方法注入属性值(要求StuService中提供set方法)-->
<!--property name="stuDao" ref="stuDao"></property>
</bean-->
<!--bean id="teamService2" class="com.lxj.StuService"-->
<!--使用构造方法注入属性值(要求提供有参构造)-->
<!--constructor-arg name="stuDao" ref="stuDao"></constructor-arg>
</bean-->
<!--按名称自动注入:查找容器中id名("stuDao")与属性名一致的对象进行注入-->
<!--bean id="teamService3" class="com.lxj.StuService" autowire="byName"></bean-->
<!--按类型自动注入:查找容器中类型与属性类型相同或者符合is-a关系的对象进行注入,但是要求类型相同的对象唯一,否则抛出异常:不知道用哪一个匹配-->
<!--bean id="teamService4" class="com.lxj.StuService" autowire="byType"></bean-->
</beans>
测试一下:
//import com.lxj.controller.StuController;
import com.lxj.dao.StudentDao;
import com.lxj.service.SService;
import com.lxj.service.StuService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test {
@Test
public void test01(){
ApplicationContext ac=new ClassPathXmlApplicationContext("application.xml");
int beanDefinitionCount = ac.getBeanDefinitionCount();
System.out.println("spring容器中对象的个数:"+beanDefinitionCount);
String[] beanDefinitionNames =ac.getBeanDefinitionNames();
System.out.println("spring容器中所有对象的名称:");
for (String name : beanDefinitionNames) {
System.out.println(name);
}
SService SService= (SService) ac.getBean("stuService");
int add = SService.add(8, "刘哥", 30);
if (add==0)
System.out.println("添加失败!");
else
System.out.println("添加成功!");
}
}
结果显示:
该创建的对象都创建了,该注入的属性都注入了,该打印的日志(切面内容)都打印了,最后数据插入成功。
本篇是个人对spring框架入门学习的基础认知,仅供参考,如有问题请及时指正!