一、概念
spring是轻量级的开源javaee框架,Spring可以解决企业应用开发的复杂性。
spring有两个核心的部分:IOC和Aop
IOC,控制反转,把创建的对象过程交给Spring进行管理
Aop,在不修改代码的前提下进行功能的增强。
Spring特点
- 方便解耦,简化开发
- 方便测试
- 方便和其他框架整合
- 方便进行事务操作
- 降低API开发难度
二、IOC容器
2.1、IOC的作用
把对象的创建和对象的调用过程交给Spring处理,这样可以降低耦合度。
在此之前我们需要导入相关的依赖,如果使用maven,直接导入依赖,或者导入相对应的jar包。
使用Spring创建对象
两种方式:基于xml配置文件,基于注解
(1)基于注解创建对象
1)创建对象
创建User类
public class User {
public void add(){
System.out.println("add....");
}
}
在src下创建一个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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置User对象属性 id方便找到对象. class对应要创建对象类的全路径(包 + 类名称)-->
<bean id="user" class="com.wyy.spring5.User">
</bean>
创建一个Test包,在包里创建一个TestSpring类,用的单元测试
@Test
public void testAdd(){
//1.加载spring配置文件,填写干刚刚创建的bean文件
BeanFactory context = new ClassPathXmlApplicationContext("bean1.xml");
//2.获取配置创建的对象,user是我们在配置bean时起的id
User user = context.getBean("user", User.class);
//3、调用相应的方法
user.add();
}
Spring提供IOC容器实现两种方式:
- (1)BeaFactory:IOC容器基本实现,是Spring内部使用,加载配置文件时候不会闯将对象,在获取的时候才会创建。
- (2)ApplicationContext:BeanFactory的子接口,多功能,面向开发人员使用。加载时候就创建。
- ApplicationContext主要实现类,
- FileSystemXmlApplicationContext
- ClassPathXmlApplicationContext
运行结果
add....
2)成员变量属性配置注入
①利用set注入
创建Book类
public class Book {
private String bname;
private String bauthor;
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
public void testDemo(){
System.out.println(bname + ":" + bauthor);
}
}
在bean文件中进行配置
<bean id="book" class="com.wyy.spring5.Book">
<!--<property name=“属性名” value=“属性值”> -->
<property name="bname" value = "《三国演义》"></property>
<property name="bauthor" value = "施耐庵"></property>
</bean>
Test类
@Test
public void testBook1(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Book book = context.getBean("book",Book.class);
book.testDemo();
}
运行结果
《三国演义》:施耐庵
②利用构造方法注入
创建Order类
public class Orders {
private String oname;
private String oaddress;
private String oprice;
//有参构造注入
public Orders(String oname, String oaddress,String oprice){
this.oname = oname;
this.oaddress = oaddress;
this.oprice = oprice;
}
public void order(){
System.out.println(oname + ":" + oaddress + ":" + oprice);
}
}
在bean中配置
<bean id="orders" class="com.wyy.spring5.Orders">
<!--<constructor-arg name=“属性名” value=“属性值”> -->
<constructor-arg name="oname" value="电脑"></constructor-arg>
<constructor-arg name="oaddress" value="china"></constructor-arg>
<!--null值 用一个<null/>代替属性值value ,值位空 -->
<constructor-arg name="oprice">
<null/>
</constructor-arg>
</bean>
Test类
@Test
public void testOeder1() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Orders orders = context.getBean("orders", Orders.class);
orders.order();
}
运行结果
电脑:china:null
3)外部注入(对象注入)
创建Dao包,在包里创建UseDao接口类和UserDaoImpl实现类
public interface UserDao {
public void update();
}
public class UserDaoImpl implements UserDao{
@Override
public void update() {
System.out.println("dao update...");
}
}
创建Service包,在包内创建UserService类
public class UserService {
//可以是属性,也可以是类对象
//创建UseDao类型属性,生成set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add(){
System.out.println("service add...");
userDao.update();
}
}
配置
因为是两个对象,我们需要建立两个bean来通过spring创建对象。
<bean id="userService" class="com.wyy.spring5.Service.UserService"></bean>
<bean id="userDaoImpl" class="com.wyy.spring5.Dao.UserDaoImpl"></bean>
UserService类中创建了UserDao属性变量,我们需要在实现UserService变量中注入UserDao对象
完整配置
<bean id="userService" class="com.wyy.spring5.Service.UserService">
<!--注入UserDao对象-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.wyy.spring5.Dao.UserDaoImpl">
</bean>
Test类
@Test
public void testBean(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
UserService userService = context.getBean("userService",UserService.class);
userService.add();
}
运行结果
service add...
dao update...
通过注入对象,当对userService方法进行定义时,可以调用userDao类的方法。
4)集合类型注入
数据类型注入:value值放在array标签中
List集合类型注入:value值放在list标签中
Map类型注入:value值放在entry标签中,标签中有 key和value两个值
Stu类
public class Stu {
//数组类型
private String[] courses;
//list集合类型
private List<String> list;
//Map集合类型
private Map<String, String> map;
//set集合
private Set<String> sets;
//学生所学的多门课程
private List<Course> courseList;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
public void testGet() {
System.out.println(Arrays.toString(courses));
System.out.println(list);
System.out.println(map);
System.out.println(sets);
System.out.println(courseList);
}
}
Course类
public class Course {
private String cname;
public Course() {
}
public void setCname(String cname) {
this.cname = cname;
}
@Override
public String toString() {
return "Course{" +
"cname='" + cname + '\'' +
'}';
}
}
配置类
<bean id="stu" class="com.wyy.spring5.collectiontype.Stu">
<!-- 数组类型-->
<property name="courses">
<array>
<value>数据库</value>
<value>数据结构</value>
</array>
</property>
<!-- List类型-->
<property name="list">
<list>
<value>张三</value>
<value>小三</value>
</list>
</property>
<!--map类型-->
<property name="map">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set注入-->
<property name="sets">
<set>
<value>mysql</value>
<value>Redis</value>
</set>
</property>
<!--注入list集合, 放 值是对象-->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<!--创建多个course对象-->
<bean id="course1" class="com.wyy.spring5.collectiontype.Course">
<property name="cname" value="Spring框架"></property>
</bean>
<bean id="course2" class="com.wyy.spring5.collectiontype.Course">
<property name="cname" value="MyBatis框架"></property>
</bean>
test类
@Test
public void testArray(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
Stu stu = applicationContext.getBean("stu", Stu.class);
stu.testGet();
}
运行结果
[数据库, 数据结构]
[张三, 小三]
{JAVA=java, PHP=php}
[mysql, Redis]
[Course{cname='Spring框架'}, Course{cname='MyBatis框架'}]
5)factoryBean接口:工厂Bean
普通的bean中:spring中配置定义的是什么类型返回的就是什么类型
工厂bean:可以返回不同类型
定义MyBean类,实现FactorBean接口
//这里填写泛型为Course
public class MyBean implements FactoryBean<Course> {
//定义返回bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
配置类
<bean id="myBean" class="com.wyy.spring5.FactoryBean.MyBean"></bean>
Test类
@Test
public void test1(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
//这里填写的是Course.class 是因为在Mybean中我们指定了泛型为Course,因为需要改写这里,否则报错,找不到相应类
Course course = context.getBean("myBean", Course.class);
System.out.println(course);
}
运行结果
Course{cname='abc'}
(2)基于注解创建对象
Spring针对Bean管理中创建对象提供注解,注解功能一样 都可以用来创建bean实例。
- (1)@Component 普通
- (2)@Service 业务逻辑层
- (3)@Cintoller web层
- (4)@Repository dao层
主要操作:
(1)导入jar,或者使用maven,添加依赖到pom文件下
(2)开启组件扫描
- 先引入context命名空间
- 扫描多个包,用逗号隔开 或 直接扫描上层目录
<!--开启组件扫描-->
<context:component-scan base-package="com.wyy"></context:component-scan>
(3)创建对象,在类上面添加注解
(4)开启组件扫描配置细节
添加use-default-filters=false表示不使用默认扫描,自己配置filter 只扫描本包下有Controller注解的类。
(5)基于注解方式实现属性注入
- @AutoWired 根据属性类行进行自动装配
- 第一步 创建service 和dao类,并添加注解
- 第二步 在service注入dao对象,在service类添加dao类型的属性,在属性上使用注解
- (2)@Qualifer 根据属性名称进行注入 value
- (3)@Resource 可以根据类型注入,可以根据名称注入 name
- (4)@value 普通类型
演示:
配置文件 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:context="http://www.springframework.org/schema/context"
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">
<!-- 开启组件扫描-->
<context:component-scan base-package="com.wyy"></context:component-scan>
</beans>
Dao包 UserDao接口和UserDaoImpl实现类
public interface UseDao {
public void add();
}
@Repository
public class UseDaoImpl implements UseDao{
@Override
public void add() {
System.out.println("dao add...");
}
}
Service包 UserService类
//注解中value属性值可以省略不写
//默认值是类名称,首字母小写
@Service(value = "userService") //value 的值等价于配置文件中bean中id的值
public class UseService {
//不需要加set方法
@Autowired
private UseDao useDao;
@Value(value = "abc")
private String name;
public void add(){
System.out.println("service add..." + name);
useDao.add();
}
}
Test包 test类
@Test
public void testAutoWire(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UseService useService = context.getBean("userService", UseService.class);
System.out.println(useService);
useService.add();
}
运行结果
service add...abc
dao add...
完全注解开发
(1)创建配置类 替代xml配置文件
@Configuration //把当前类作为xml配置文件
@ComponentScan(basePackages ={"com.wyy"})//类似扫描组件
public class SpringConfig {
}
(2)Test测试类
注意与前面不同
@Test
public void testConnfig(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UseService useService = context.getBean("userService", UseService.class);
System.out.println(useService);
useService.add();
}
运行结果
service add...abc
dao add...
2.2 Aop
Aop是面向切面编程,不通过修改源代码的方式,在主干功能里面添加新的功能
(1)AOP底层原理是使用动态代理,有两种动态代理方式
- 第一种,有接口,使用JDK动态代理
- 第二种,无接口,使用CGIVB代理
(2)实现动态代理演示
JDk动态代理,使用Proxy类里面的方法创建动态代理类的对象,也调用newProxyInstance方法。
- 第一个参数 类加载器 当前类接
- 第二个参数 增强方法所在类的接口字节码,用数组形式来表示
- 第三个参数 实现接口InvocationHandler ,创建代理对象,写增强方法 proxy实现类的实例对象。
userDao接口类
在接口种定义了两个方法,一个有参有返回值,一个无参无返回值,
public interface UserDao {
public int add(int a, int b);
public void update();
}
userDaoImpl实现类
对user中的方法进行实现。
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
System.out.println("add执行了...");
return a+b;
}
@Override
public void update() {
System.out.println("update执行了...");
}
}
JDKProxy代理类(主要)
在类中定义一个给userDaoImpl实现类,创建一个代理。
public class JDKProxy {
public static UserDao createProxy(UserDaoImpl userDao ){
UserDao user = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),
new Class[]{UserDao.class},//对应接口的数组
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("add".equals(method.getName())){
System.out.println("准备计算");
}else if ("update".equals(method.getName())){
System.out.println("准备更新");
}
//返回调用方法的返回值
return method.invoke(userDao ,args);
}
});
return user;
}
}
Test类
@Test
public void test3(){
//1.获取代理对象
UserDaoImpl userDao = new UserDaoImpl();
UserDao proxy = JDKProxy.createProxy(userDao);
//2.调用方法 有参有返回值
int add = proxy.add(2, 4);
System.out.println(add);
//3.调用方法 无惨无返回值
proxy.update();
}
运算结果
准备计算
add执行了...
6
准备更新
update执行了...
(3)AOP术语
- 1)连接点: 类那些方法可以被增强,这些方法称为连接点
- 2)切入点:实际被增强的方法,称为切入点
- 3)通知(增强)
- 实际增强的逻辑部分被叫做通知 代码
- 类型
- 前置通知:@before
- 后置通知:@After
- 环绕通知:前后都有 @Around
- 异常通知:异常 @afterThrowing
- 最终通知:无论怎样都会在最后进行通知 @afterReturning
- 优先级:(从高到低)Around/ before /after /afterReturning/ afterThrowing
- 4)切面:动作,把通知应用到切入点的过程
(4)Aop在Spring中的操作
1)spring框架一般都是基于AspectJ实现Aop操作,但AspectJ不属于Spring框架
2)基于AspectJ实现Aop操作
①基于xml配置
要被增强的Book类
public class Book {
public void add(){
System.out.println("add...");
}
}
BookProxy代理类
public class BookProxy {
public void before(){
System.out.println("before.add..");
}
}
配置类(关键)
<?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">
<bean id="book" class="com.wyy.spring5.aopxml.Book"></bean>
<bean id="bookProxyk" class="com.wyy.spring5.aopxml.BookProxy"></bean>
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.wyy.spring5.aopxml.Book.add(..))"/>
<!--配置切面-->
<aop:aspect ref="bookProxyk">
<!--增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
</beans>
运行结果
before.add..
add...
②基于注解
配置类
<?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 base-package="com.wyy.spring5.aopannno"></context:component-scan>
<!--开启aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
要被增强的User类
@Component
public class User {
public void add(){
System.out.println("add...");
}
}
增强类UserProxy类
切入点表达式
切入点表达式作用:知道对哪个类里面的哪个方法进行增强
execution([权限修饰符][返回类型][类全路劲][方法名称]([参数列表]))
使用 .. 表示任意个数、任意类型的参数
@Component
@Aspect//生成代理对象
public class UserProxy {
//前置通知 value是指要被增强类的方法
@Before(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
public void before(){
System.out.println("before...");
}
@After(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
public void after(){
System.out.println("after...");
}
@AfterReturning(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning...");
}
@AfterThrowing(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing...");
}
//当被增强的方法是同一个时,可以对value的值进行抽取(相同切入点抽取)
//@Pointcut(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
//public void pointdemp(){}
//之后可以这样写
//@Before(value = pointdemp())
//public void before(){
//System.out.println("before...");
//}
}
Test类
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user", User.class);
user.add();
}
运行结果
before...
add...
after...
afterReturning...
多个增强类对同一个方法进行增强,可以设置优先级,在增强类上面填注解 @order (数字类型值)数字越小 ,优先级越高。
完全注解
添加配置类
@Configuration
@ComponentScan(basePackages = {"com.wyy"})
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启开启aspect生成代理对象
public class ConfigAop {
}