冯宝宝说:Spring
Spring概述
1.1. 简介
Spring : 春天 --->给软件行业带来了春天
2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。
很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术
SSH:Struct2 + Spring + Hibernate
SSM: SpringMVC + Spring + Mybatis
官网 : Spring | Home
官方下载地址 : JFrog
GitHub : Spring · GitHub
Spring Web MVC » 5.2.5.RELEASE
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.5.RELEASE</version>
</dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.3.RELEASE</version>
</dependency>
1.2. 优点
spring是开源的免费的容器。
spring是一个轻量级的,非入侵式的。
控制反转(IOC),面向切面编程 (AOP)。
支持事务处理,对框架整合的支持。
总结:spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。
1.3. 组成
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式 .
解释:
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
1.4. 拓展
在Spring的官网有这个介绍:现代化的java开发!说白了就是基于Spring的开发!
Spring Boot
一个快速开发的脚手架
基于Spring Boot 快速开发单个微服务
Spring Cloud
Spring Cloud是基于Spring Boot实现的。
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用!
弊端:发展了太久了之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”
2. IOC理论推导
新建一个空白的maven项目
分析实现
我们先用我们原来的方式写一段代码 .
1、先写一个UserDao接口
public interface UserDao { public void getUser();
}
2、再去写Dao的实现类
public class UserDaoImpl implements UserDao { @Override public void getUser() { System.out.println("获取用户数据"); }
}
3、然后去写UserService的接口
public interface UserService { public void getUser();
}
4、最后写Service的实现类
public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); @Override public void getUser() { userDao.getUser(); }
}
5、测试一下
@Testpublic void test(){ UserService service = new UserServiceImpl(); service.getUser();
}
这是我们原来的方式 , 开始大家也都是这么去写的对吧 . 那我们现在修改一下 .
把Userdao的实现类增加一个 .
public class UserDaoMySqlImpl implements UserDao { @Override public void getUser() { System.out.println("MySql获取用户数据"); }
}
紧接着我们要去使用MySql的话 , 我们就需要去service实现类里面修改对应的实现
public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoMySqlImpl(); @Override public void getUser() { userDao.getUser(); }
}
在假设, 我们再增加一个Userdao的实现类 .
public class UserDaoOracleImpl implements UserDao { @Override public void getUser() { System.out.println("Oracle获取用户数据"); }
}
那么我们要使用Oracle , 又需要去service实现类里面修改对应的实现 . 假设我们的这种需求非常大 , 这种方式就根本不适用了, 甚至反人类对吧 , 每次变动 , 都需要修改大量代码 . 这种设计的耦合性太高了, 牵一发而动全身 .
那我们如何去解决呢 ?
我们可以在需要用到他的地方 , 不去实现它 , 而是留出一个接口 , 利用set , 我们去代码里修改下 .
public class UserServiceImpl implements UserService { private UserDao userDao;// 利用set实现 public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void getUser() { userDao.getUser(); }
}
现在去我们的测试类里 , 进行测试 ;
@Test
public void test(){//用户实际调用的是业务层,dao层他们不需要接触!UserServiceImpl service = new UserServiceImpl();service.setUserDao( new UserDaoMySqlImpl() );service.getUser();//那我们现在又想用Oracle去实现呢service.setUserDao( new UserDaoOracleImpl() );service.getUser();
}
大家发现了区别没有 ?
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价十分昂贵!
我们使用一个set接口实现,已经发生了革命性的变化!
private UserDao userDao;
//利用set进行动态实现值的注入!public void setUserDao(UserDao userDao){this.userDao = userDao;}
之前,程序员是主动创建对象!控制权在程序员手上!
使用了set注入之后,程序不在具有主动性,而是变成了被动的接受对象!
这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 可以更加专注的在业务的实现上,这也就是IOC的原型 !
IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3. HelloSpring
快速上手 HelloSpring
导入Jar包
1、编写一个Hello实体类
public class Hello {private String name;
public String getName() {return name;}public void setName(String name) {this.name = name;}
public void show(){System.out.println("Hello,"+ name );}
@Overridepublic String toString() {return "Hello{" +"name='" + name + '\'' +'}';}
}
2、编写我们的spring文件 , 这里我们命名为beans.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean--><!--该id属性是一个字符串,用于标识单个 bean 定义。该class属性定义 bean 的类型并使用完全限定的类名。类型 变量名 = new 类型();Hello hello = new Hello();id = 变量名class = new 的对象;property 相当于给对象中的属性设置一个值!--><bean id="hello" class="com.lxyk.pojo.Hello"><property name="name" value="Spring"/></bean>
</beans>
3,测试
public class MyTest {public static void main(String[] args) {//获取Spring的上下文对象!ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");//我们的对象现在都在Spring中的管理了 ,我们要使用,直接去;里面取出来就可以!Hello hello = (Hello) context.getBean("hello");System.out.println(hello.toString());hello.show();}
}
思考问题?
Hello 对象是谁创建的 ?
hello 对象是由Spring创建的
Hello 对象的属性是怎么设置的 ?
hello 对象的属性是由Spring容器设置的
这个过程就叫控制反转 :
控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
依赖注入 : 就是利用set方法来进行注入的.
IOC是一种编程思想,由主动的编程变成被动的接收
可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .
修改案例一
spring-01-ioc1
我们在案例一中, 新增一个Spring配置文件beans.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="mysqlImpl" class="com.lxyk.dao.UserDaoMySqlImpl"/><bean id="oracleImpl" class="com.lxyk.dao.UserDaoOracleImpl"/>
<bean id="UserServiceImpl" class="com.lxyk.service.UserServiceImpl"><!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写--><!--引用另外一个bean , 不是用value 而是用 ref--><!--ref:引用Spring容器中创建好的对象value:具体的值,基本数据类型!--><property name="userDao" ref="mysqlImpl"/></bean>
</beans>
测试!
public class MyTest {public static void main(String[] args) {/*//用户实际调用的是业务层,dao层他们不需要接触!UserServiceImpl service = new UserServiceImpl();service.setUserDao(new UserDaoImpl());service.getUser();*/
//获取ApplicationContext 拿到Spring的容器ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//容器在手,天下我有,需要什么,就直接get什么!UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");userServiceImpl.getUser();}
}
OK,到了现在,我们彻底不用再初中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的Ioc,一句话搞定:对象由Spring来创建,管理,装配!
4.IOC创建对象方式
1.使用无参构造创建对象,默认!
1、User.java
public class User { private String name; public User() { System.out.println("user无参构造方法"); } public String getName() {return name;}public void setName(String name) { this.name = name; } public void show(){ System.out.println("name="+ name ); }
}
2、beans.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.lxyk.pojo.User"><property name="name" value="冯宝宝"/></bean>
</beans>
3、测试类
public class MyTest {public static void main(String[] args) {//User user = new User();ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");User user = (User) context.getBean("user");user.show();
}
}
结果可以发现,在调用show方法之前,User对象已经通过无参构造初始化了!
Debug查看代码怎么走的!
属性除了可以直接初始化之外,我们也可以通过构造器来进行初始化
2.假设我们要使用有参构造创建对象!
1.下标赋值
<!--第一种方式,下标赋值--><bean id="user" class="com.lxyk.pojo.User"><constructor-arg index="0" value="冯宝宝说java"/></bean>
2.类型
<!--第二种方式:通过类型创建,不建议使用!--><bean id="user" class="com.lxyk.pojo.User"><constructor-arg type="java.lang.String" value="冯宝宝说Spring"/></bean>
3.参数名
<!--第三种:直接通过参数名来设置--><bean id="user" class="com.lxyk.pojo.User"><constructor-arg name="name" value="冯宝宝"/></bean>
测试一个好玩的
UserT
public class UserT {private String name;
public UserT() {System.out.println("userT被创建了");}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public void show(){System.out.println("name="+ name );}
}
beans.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd">
<!--第三种:直接通过参数名来设置--><bean id="user" class="com.lxyk.pojo.User"><constructor-arg name="name" value="冯宝宝"/></bean>
<bean id="userT" class="com.lxyk.pojo.UserT">
</bean>
</beans>
测试
public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");User user = (User) context.getBean("user");user.show();//spring容器,就类似于婚介网站!
User user2 = (User) context.getBean("user");System.out.println(user == user2);//说明内存中只有一个实例,取的都是哪一个 出来的时候就被创建了
}
}
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!
5. Spring配置
5.1.别名
<!--別名,如果添加了別名,我們也可以使用別名获取到这个对象--><alias name="user" alias="userNew"/>
5.2.Bean的配置
<!--id是bean的标识符,要唯一,也就是相当于我们学的对象名class是bean的全限定名=包名+类名name:也是别名,而且name 可以同时取多个别名--><bean id="userT" class="com.lxyk.pojo.UserT" name="userT2 u2,u3;u4"><property name="name" value="乐学优课"/></bean>
5.3.import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
加上,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有的人的beans.xml合并为一个总的!
张三
李四
王五
applicationContext.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="beans.xml"/><import resource="beans2.xml"/> </beans>
使用的时候,直接使用总的配置就可以了
6. DI依赖注入
6.1.构造器注入
前面已经说过了
6.2.Set方式注入【重点】
依赖注入:Set注入!
依赖 : 指Bean对象的创建依赖于容器!
注入 : 指Bean对象中的所有属性,由容器来注入!
【环境搭建】
1.复制类型
Address.java
public class Address {private String address;
public String getAddress() {return address;}
public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", address=" + address.toString() +", books=" + Arrays.toString(books) +", hobbys=" + hobbys +", card=" + card +", games=" + games +", wife='" + wife + '\'' +", info=" + info +'}';}
}
2.真实测试对象
Student.java
public class Student {
private String name;private Address address;private String[] books;private List<String> hobbys;private Map<String,String> card;private Set<String> games;private String wife;private Properties info;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Address getAddress() {return address;}
public void setAddress(Address address) {this.address = address;}
public String[] getBooks() {return books;}
public void setBooks(String[] books) {this.books = books;}
public List<String> getHobbys() {return hobbys;}
public void setHobbys(List<String> hobbys) {this.hobbys = hobbys;}
public Map<String, String> getCard() {return card;}
public void setCard(Map<String, String> card) {this.card = card;}
public Set<String> getGames() {return games;}
public void setGames(Set<String> games) {this.games = games;}
public String getWife() {return wife;}
public void setWife(String wife) {this.wife = wife;}
public Properties getInfo() {return info;}
public void setInfo(Properties info) {this.info = info;}
@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", address=" + address.toString() +", books=" + Arrays.toString(books) +", hobbys=" + hobbys +", card=" + card +", games=" + games +", wife='" + wife + '\'' +", info=" + info +'}';}
}
applicationContext.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.lxyk.pojo.Student"><property name="name" value="冯宝宝"/></bean>
</beans>
测试类
public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Student student = (Student) context.getBean("student");System.out.println(student.getName());}
}
完善注入信息
applicationContext.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.lxyk.pojo.Address"><property name="address" value="武汉"/></bean>
<bean id="student" class="com.lxyk.pojo.Student"><!--第一种,普通值注入方式 value--><property name="name" value="冯宝宝"/>
<!--第二种,Bean注入,ref--><property name="address" ref="address"/>
<!--数组注入,ref--><property name="books"><array><value>红楼梦</value><value>西游记</value><value>水浒传</value><value>三国演义</value></array></property>
<!--List数组--><property name="hobbys"><list><value>听歌</value><value>看电影</value><value>旅游</value></list></property>
<!--Map--><property name="card"><map><entry key="身份证" value="1111112222222334"/><entry key="银行卡" value="9999999988888888"/></map></property>
<!--Set--><property name="games"><set><value>LOL</value><value>DNF</value><value>CF</value></set></property>
<!--null--><property name="wife"><null/></property>
<!--Properties--><property name="info"><props><prop key="学号">2021999 </prop><prop key="姓名">小米 </prop><prop key="性别">女 </prop></props></property></bean>
</beans>
测试
public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Student student = (Student) context.getBean("student");//System.out.println(student.getName());System.out.println(student.toString());
/*** Student{name='冯宝宝',* address=Address{address='武汉'},* books=[红楼梦, 西游记, 水浒传, 三国演义],* hobbys=[听歌, 看电影, 旅游],* card={身份证=1111112222222334, 银行卡=9999999988888888},* games=[LOL, DNF, CF],* wife='null',* info={学号=2021999, 性别=女, 姓名=小米}}*/
}
}
6.3.拓展方式注入
p命名和c命名注入
1、P命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<!--P命名空间注入,可以直接注入属性的值:property--><bean id="user" class="com.lxyk.pojo.User" p:name="冯宝宝" p:age="18"/>
测试
@Testpublic void test(){ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");User user = context.getBean("user", User.class);System.out.println(user);}
2、c 命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<!--c命名空间注入,通过构造器注入;construct-args--><bean id="user2" class="com.lxyk.pojo.User" c:name="小米" c:age="18" />
发现问题:爆红了,刚才我们没有写有参构造,无参构造!
public User() {}
public User(String name, int age) {this.name = name;this.age = age;}
解决:把有参构造器加上,这里也能知道,c 就是所谓的构造器注入!
测试
@Testpublic void test(){ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");User user = context.getBean("user2", User.class);System.out.println(user);}
6.4.Bean的作用域
在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象 .
几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。
Singleton
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
1.单列模式(Spring默认机制)
<bean id="user2" class="com.lxyk.pojo.User" c:name="小米" c:age="18" scope="singleton"/>
测试
@Testpublic void test03(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");User user = (User) context.getBean("user");User user2 = (User) context.getBean("user");System.out.println(user==user2);}
Prototype
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
2.原型模式:每次从容器get的时候,都会产生一个新对象
<bean id="user2" class="com.lxyk.pojo.User" c:name="小米" c:age="18" scope="prototype"/>
测试
@Testpublic void test(){ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");User user1 = context.getBean("user2", User.class);User user2 = context.getBean("user2", User.class);System.out.println(user1.hashCode());System.out.println( user2.hashCode());
System.out.println(user1 == user2);}
3.其余的request,session,application,这些个只能在web开发中使用到!
7.Bean的自动装配
自动装配是使用spring满足bean依赖的一种方法
spring会在应用上下文中为某个bean寻找其依赖的bean。
Spring中bean有三种装配机制,分别是:
在xml中显式配置;
在java中显式配置;
隐式的bean发现机制和自动装配。【重要】
7.1.测试环境搭建
一个人有两个宠物
1、新建一个项目
2、新建两个实体类,Cat Dog 都有一个叫的方法
public class Cat {public void shout() {System.out.println("miao~");}
}
public class Dog {public void shout() {System.out.println("wang~");}
}
3、新建一个用户类 User
public class User {private Cat cat;private Dog dog;private String name;
}
4、编写Spring配置文件
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" class="com.lxyk.pojo.Dog"/><bean id="cat" class="com.lxyk.pojo.Cat"/>
<bean id="user" class="com.lxyk.pojo.User"><property name="cat" ref="cat"/><property name="dog" ref="dog"/><property name="name" value="冯宝宝"/></bean>
</beans>
测试
public class MyTest {@Testpublic void test(){ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");User user = context.getBean("user", User.class);user.getCat().shout();user.getDog().shout();System.out.println(user.getName());}
}
结果正常输出,环境OK(这种方法是显示的在xml文件中装配)
7.2. byName 自动装配
autowire byName (按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
采用自动装配将避免这些错误,并且使配置简单化。
测试:
1、修改bean配置,增加一个属性 autowire="byName"
<!--byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid !--><bean id="user" class="com.lxyk.pojo.User" autowire="byName"><property name="name" value="冯宝宝"/></bean>
2、再次测试,结果依旧成功输出!
3、我们将 cat 的bean id修改为 catXXX
4、再次测试, 执行时报空指针java.lang.NullPointerException。因为按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。
小结:
当一个bean节点带有 autowire byName的属性时。
将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
去spring容器中寻找是否有此字符串名称id的对象。
如果有,就取出注入;如果没有,就报空指针异常。
7.3.byType 自动装配
autowire byType (按类型自动装配)
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
NoUniqueBeanDefinitionException
测试:
1、将user的bean配置修改一下 : autowire="byType"
2、测试,正常输出
3、在注册一个cat 的bean对象!
<bean class="com.lxyk.pojo.Dog"/><bean class="com.lxyk.pojo.Cat"/>
<!--byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid !byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!--><bean id="user" class="com.lxyk.pojo.User" autowire="byType"><property name="name" value="冯宝宝"/></bean>
4、测试,报错:NoUniqueBeanDefinitionException
5、删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。
这就是按照类型自动装配!
小结
byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性set方法的值一致!
bytype的时候,需要保证有bean的class唯一,并且这个bean需要和自动注入的属性的类型值一致!
7.4.使用注解实现自动装配
jdk1.5支持的注解,Spring2.5就支持注解了!
要使用注解须知:
导入约束:context约束
配置注解的支持:context:annotation-config/
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
@Autowired
直接在属性上使用即可!也可以在set方法上使用!
使用Autowired我们可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符名字byname!
public class User {@Autowiredprivate Cat cat;@Autowiredprivate Dog dog;private String name;
}
beans.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解的支持--><context:annotation-config/>
<bean id="dog" class="com.lxyk.pojo.Dog"/><bean id="cat" class="com.lxyk.pojo.Cat"/><bean id="user" class="com.lxyk.pojo.User"/>
</beans>
如果@Autowired自动装配环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value="xxx")去配置@Autowired的使用,指定一个唯一的bean对象注入!
public class User {@Autowired@Qualifier(value = "cat222")private Cat cat;@Autowiredprivate Dog dog;private String name;
}
@Resource注解
public class User {@Resource(name = "cat22")private Cat cat;@Resourceprivate Dog dog;private String name;}
小结
@Resource和@Autowired区别:
都是用来自动装配的,都可以放在属性字段上
@Autowired通过byType的方式实现,而且必须要求这个对象存在!【常用】
@Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!
执行顺序不同:@Autowired通过byType的方式实现,@Resource默认通过byname的方式实现。
8.使用注解开发
在spring4之后,想要使用注解形式,必须得要引入aop的包
使用注解需要导入context约束,增加注解的支持
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--注解驱动--><context:annotation-config/></beans>
1.bean
1、配置扫描哪些包下的注解
<!--指定要扫描的包,这个包下面的注解就会生效--><context:component-scan base-package="com.lxyk.pojo"/>
2、在指定包下编写类,增加注解
//等价与 <bean id="user" class="com.lxyk.pojo.User"/>
//组件
@Component
public class User {public String name = "冯宝宝";
}
3、测试
public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");User user = context.getBean("user", User.class);System.out.println(user.name);}
}
2.属性如何注入
使用注解注入属性
1、可以不用提供set方法,直接在直接名上添加@value("值")
//等价与 <bean id="user" class="com.lxyk.pojo.User"/>
//组件
@Component
public class User {// 相当于配置文件中 <property name="name" value="冯宝宝"/>@Value("冯宝宝")public String name;
}
2、如果提供了set方法,在set方法上添加@value("值")
//等价与 <bean id="user" class="com.lxyk.pojo.User"/>
//组件
@Component
public class User {
public String name;
// 相当于配置文件中 <property name="name" value="冯宝宝"/>@Value("冯宝宝")public void setName(String name) {this.name = name;}
}
我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
3.衍生的注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
@Component 有几个衍生注解,我们在web开发中,会按照mvc三次架构分层!
dao层 【 @Repository 】
service 【 @Service 】
web层(controller) 【 @Controller 】
写上这些注解,就相当于将这个类交给Spring管理装配了!
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean
4.自动装配置
在Bean的自动装配已经讲过了,可以回顾!
@Autowired:自动装配通过类型,名字。
如果@Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")
@Nullable:字段标记了这个注解,说明这个字段可以为null;
@Resource:自动装配通过名字,类型。
5.作用域
@scope
singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
@Controller("user")
@Scope("prototype")
public class User {@Value("冯宝宝")public String name;
}
6.小结
XML与注解比较
XML更加万能,可以适用任何场景 ,结构清晰,维护方便
注解不是自己提供的类使用不了,维护相对复杂,开发简单方便
xml与注解整合开发 :推荐最佳实践
xml管理Bean
注解完成属性注入
使用过程中, 可以不用扫描,扫描是为了类上的注解,注意一个问题:必须让注解生效,就需要开启注解的支持
<!--指定要扫描的包,这个包下面的注解就会生效--><context:component-scan base-package="com.lxyk"/><!--注解驱动--><context:annotation-config/>
作用:
进行注解驱动注册,从而使注解生效
用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
如果不扫描包,就需要手动配置bean
如果不加注解驱动,则注入的值为null!
9.使用java的方式配置Spring
我们现在要完全不使用Spring的xml配置,全权交给java来做!
javaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!
1、编写一个实体类,User
//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中
@Component
public class User {private String name;
public String getName() {return name;}
//属性注入值@Value("冯宝宝")public void setName(String name) {this.name = name;}
@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +'}';}
}
2、新建一个config配置包,编写一个MyConfig配置类
// 这个也会被Spring容器托管,注册到容器中,因为他本身就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml
@Configuration
@ComponentScan("com.lxyk.pojo")
public class MyConfig {
//注册一个bean,就相当于我们之前写的一个bean标签//这个方法的名字,就相当于bean标签中的id属性//这个方法的返回值,就相当于bean标签中的class属性@Beanpublic User getUser(){return new User(); //就是返回要注入到bean的对象!}
}
3、测试
public class MyTest {public static void main(String[] args) {//如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);User user = (User) context.getBean("getUser");System.out.println(user.getName());}
}
导入其他配置如何做呢?
1、我们再编写一个配置类
@Configuration //代表这是一个配置类
public class MyConfig2 {
}
2、在之前的配置类中我们来选择导入这个配置类
// 这个也会被Spring容器托管,注册到容器中,因为他本身就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml
@Configuration
@ComponentScan("com.lxyk.pojo")
@Import(MyConfig2.class)
public class MyConfig {
//注册一个bean,就相当于我们之前写的一个bean标签//这个方法的名字,就相当于bean标签中的id属性//这个方法的返回值,就相当于bean标签中的class属性@Beanpublic User getUser(){return new User(); //就是返回要注入到bean的对象!}
}
这种存java的配置方式,在SpringBoot中随处可见!
10.代理模式
为什么要学习代理模式?因为A OP的底层机制就是动态代理!
代理模式:
静态代理
动态代理
学习aop之前 , 我们要先了解一下代理模式!
10.1.静态代理
静态代理角色分析
抽象角色 : 一般使用接口或者抽象类来实现
真实角色 : 被代理的角色
代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
客户 : 访问代理对象的人!
Rent . java 即抽象角色
//抽象角色:租房
public interface Rent {public void rent();
}
Host . java 即真实角色
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {System.out.println("房东要出租房子........");}
}
Proxy . java 即代理角色
//代理角色:中介
public class Proxy implements Rent{
private Host host;
public Proxy() {}
public Proxy(Host host) {this.host = host;}
public void rent() {seeHouse();host.rent();hetong();fare();}
//看房子public void seeHouse(){System.out.println("中介带你看房......");}
//合同public void hetong(){System.out.println("签租赁合同......");}
//收中介费public void fare(){System.out.println("收中介费......");}
}
Client . java 即客户
//客户类,一般客户都会去找代理!
public class Client {public static void main(String[] args) {//房东要租房子Host host = new Host();//代理,中介帮房东租房子,但是呢?代理一般会有一些附属操作!Proxy proxy = new Proxy(host);
//你不用面对房东,直接找中介租房即可!proxy.rent();
}
}
分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。
程序来源于生活,程序服务于生活,面向对象,面向百度,面向生活编程!
静态代理的好处:
可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
公共的业务由代理来完成 . 实现了业务的分工 !
公共业务发生扩展的时候,方便集中管理 !
缺点 :
类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
一个真实角色就会产生一个代理角色;代码量就会翻倍~开发效率会变低~
10.2.加深理解
UserService
public interface UserService {void add();void delete();void update();void query();
}
UserServiceImpl
//真实对象
public class UserServiceImpl implements UserService{public void add() {System.out.println("增加了一个用户");}
public void delete() {System.out.println("删除了一个用户");}
public void update() {System.out.println("修改了一个用户");}
public void query() {System.out.println("查询了一个用户");}}
测试
public class Client {public static void main(String[] args) {UserServiceImpl userService = new UserServiceImpl();userService.add();}
}
代理
public class UserServiceProxy implements UserService{private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {this.userService = userService;}
public void add() {log("add");userService.add();}
public void delete() {log("delete");userService.delete();}
public void update() {log("update");userService.update();}
public void query() {log("query");userService.query();}
//日志方法public void log(String msg){System.out.println("[Debug]使用了"+msg+"方法");}
}
测试
public class Client {public static void main(String[] args) {UserServiceImpl userService = new UserServiceImpl();//userService.add();
UserServiceProxy proxy = new UserServiceProxy();proxy.setUserService(userService);
proxy.add();}
}
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想;
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
聊聊AOP:纵向开发,横向开发
10.3.动态代理
动态代理的角色和静态代理的一样 .
动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
基于接口的动态代理----JDK动态代理
基于类的动态代理--cglib
java字节码:现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
我们这里使用JDK的原生代码来实现,其余的道理都是一样的!、
JDK的动态代理需要了解两个类
核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看
【InvocationHandler:调用处理程序】
代码实现
抽象角色和真实角色和之前的一样!
Rent . java 即抽象角色
//抽象角色:租房
public interface Rent {public void rent();
}
Host . java 即真实角色
//真实角色: 房东,房东要出租房子
public class Host implements Rent{public void rent() {System.out.println("房屋出租");}
}
ProxyInvocationHandler. java 即代理角色
public class ProxyInvocationHandler implements InvocationHandler {private Rent rent;
public void setRent(Rent rent) {this.rent = rent;}
//生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色public Object getProxy(){return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);}
// proxy : 代理类 method : 代理类的调用处理程序的方法对象.// 处理代理实例上的方法调用并返回结果@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {seeHouse();//核心:本质利用反射实现!Object result = method.invoke(rent, args);fare();return result;}
//看房public void seeHouse(){System.out.println("带房客看房");}//收中介费public void fare(){System.out.println("收中介费");}
}
Client . java
public class Client {
public static void main(String[] args) {//真实角色Host host = new Host();//代理角色:现在没有//代理实例的调用处理程序ProxyInvocationHandler pih = new ProxyInvocationHandler();pih.setRent(host); //将真实角色放置进去!Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!proxy.rent();}
}
核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!、
10.4.深化理解
我们来使用动态代理实现代理我们后面写的UserService!
我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!
ProxyInvocationHandler.java
private Object target;
public void setTarget(Object target) {this.target = target;}
//生成代理类public Object getProxy(){return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}
// proxy : 代理类// method : 代理类的调用处理程序的方法对象.public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {log(method.getName());Object result = method.invoke(target, args);return result;}
public void log(String msg){System.out.println("执行了"+msg+"方法");}
}
测试!
public class Client {public static void main(String[] args) {//真实对象UserServiceImpl userService = new UserServiceImpl();
//代理对象的调用处理程序ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService); //设置要代理的对象
//动态生成代理类!UserService proxy = (UserService)pih.getProxy();
proxy.add();}
}
测试,增删改查,查看结果!
动态代理的好处
静态代理有的它都有,静态代理没有的,它也有!
可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
公共的业务由代理来完成 . 实现了业务的分工 ,
公共业务发生扩展时变得更加集中和方便 .
一个动态代理 , 一般代理某一类业务
一个动态代理可以代理多个类,代理的是接口!
静态代理代理的接口是写死的,只能代理该类接口的实现类,实现其对应的方法,真实角色都属于同一类,动态代理通过反射获取被代理对象的接口类型,所以代理的对象可以是任意的,代理类都可以通过反射获取接口类型,被代理对象的方法也可以通过反射获取,所以动态代理相比静态代理最大的区别就是能够代理的类型可以是任意(Object)的,而静态代理只能代理一类对象(实现相同的接口),静态代理实现了和被代理类相同的接口,而动态代理实现的是InvocationHandler ,可以通过反射实现任意类型的接口,更具灵活性。
11.AOP
11.1.什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
11.2. AOP在Spring中的作用
提供声明式事务;允许用户自定义切面
以下名词需要了解下:
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
切入点(PointCut):切面通知 执行的 “地点”的定义。
连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .
11.3.使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包!
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version>
</dependency>
第一种方式
通过 Spring API 实现【主要是SpringAPI接口实现】
首先编写我们的业务接口和实现类
public interface UserService {void add();void delete();void update();void select();
}
public class UserServiceImpl implements UserService{
public void add() {System.out.println("增加了一个用户");}
public void delete() {System.out.println("删除了一个用户");}
public void update() {System.out.println("修改了一个用户");}
public void select() {System.out.println("查询了一个用户");}
}
然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强
public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法//objects : 被调用的方法的参数//Object : 目标对象public void before(Method method, Object[] objects, Object o) throws Throwable {System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");}
}
public class AfterLog implements AfterReturningAdvice {//returnValue 返回值//method被调用的方法//args 被调用的方法的对象的参数//target 被调用的目标对象public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("执行了" + target.getClass().getName()+"的"+method.getName()+"方法,"+"返回结果为:"+returnValue);}
}
最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .
例如定义切入点表达式 execution (* com.sample.service.impl..*. *(..))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型, *号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个号:表示类名,号表示所有的类。
5、(..):最后这个星号表示方法名,号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean--><bean id="userService" class="com.lxyk.service.UserServiceImpl"/><bean id="log" class="com.lxyk.log.Log"/><bean id="afterLog" class="com.lxyk.log.AfterLog"/>
<!--方式一:使用原生Spring API接口--><!--配置aop,需要导入aop的约束--><aop:config><!--切入点:expression:表达式,execution(要执行的位置! * * * * *)--><!--(* com.lxyk.service.UserServiceImpl.*(..):类下面的方法,方法有不同的参数..就是所有参数--><aop:pointcut id="pointcut" expression="execution(* com.lxyk.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加!--><!--执行环绕; advice-ref执行方法 . pointcut-ref切入点--><aop:advisor advice-ref="log" pointcut-ref="pointcut"/><aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/></aop:config>
</beans>
测试
public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//动态代理代理的是接口!注意点UserService userService = (UserService) context.getBean("userService");
userService.add();}
}
Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .
Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理
第二种方式
自定义类来实现Aop【主要是切面定义】
目标业务类不变依旧是userServiceImpl
第一步 : 写我们自己的一个切入类
public class DiyPointcut {public void before(){System.out.println("---------方法执行前---------");}public void after(){System.out.println("---------方法执行后---------");}
}
去spring中配置
<!--方式二:自定义类--><bean id="diy" class="com.lxyk.diy.DiyPointcut"/>
<aop:config><!--自定义切面,ref要引用类--><aop:aspect ref="diy"><!--切入点--><aop:pointcut id="point" expression="execution(* com.lxyk.service.UserServiceImpl.*(..))"/><!--通知--><aop:before method="before" pointcut-ref="point"/><aop:after method="after" pointcut-ref="point"/></aop:aspect></aop:config>
测试
public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//动态代理代理的是接口!注意点UserService userService = (UserService) context.getBean("userService");
userService.add();}
}
第三种方式
使用注解实现
第一步:编写一个注解实现的增强类
//注解方式实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPointcut {@Before("execution(* com.lxyk.service.UserServiceImpl.*(..))")public void before(){System.out.println("---------方法执行前---------");}
@After("execution(* com.lxyk.service.UserServiceImpl.*(..))")public void after(){System.out.println("---------方法执行后---------");}
//在环绕增强中,我可以给定一个参数,代表我们要获取处理切入的点:@Around("execution(* com.lxyk.service.UserServiceImpl.*(..))")public void around(ProceedingJoinPoint jp) throws Throwable {System.out.println("环绕前");//获得签名System.out.println("签名:"+jp.getSignature());
//执行目标方法proceedObject proceed = jp.proceed();
System.out.println("环绕后");System.out.println(proceed);}
}
第二步:在Spring配置文件中,注册bean,并增加支持注解的配置
<!--方式三--><bean id="annotationPointcut" class="com.lxyk.diy.AnnotationPointcut"/><!--开启注解支持! JDK(默认 proxy-target-class="false" ) cglib(proxy-target-class="true")--><aop:aspectj-autoproxy/>
aop:aspectj-autoproxy:说明
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了 <aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
测试
public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//动态代理代理的是接口!注意点UserService userService = (UserService) context.getBean("userService");
userService.add();}
}
12.整合Mybatis
步骤:
1.导入相关jar包
<dependencies><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><!--spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.12.RELEASE</version></dependency><!--spring操作数据库的话,还需要一个Spring-jdbc--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.12.RELEASE</version></dependency><!--aop--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency><!--mybatis-spring整合包--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.6</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency></dependencies>
2.编写配置文件
3.测试
12.1.回忆Mybatis
1.编写实体类
@Data
public class User {private int id; //idprivate String name; //姓名private String pwd; //密码
}
2.编写核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration:核心配置文件-->
<configuration>
<!--给实体类起别名--><typeAliases><package name="com.lxyk.pojo"/></typeAliases>
<environments default="development"><environment id="development"><!--transactionManager:事务管理--><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments>
<!--每一个mapper.xml 都需要在Mybatis核心配置文件中注册--><mappers><mapper class="com.lxyk.mapper.UserMapper"/></mappers>
</configuration>
3.编写接口
public interface UserMapper {public List<User> selectUser();
}
4.编写Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lxyk.mapper.UserMapper">
<select id="selectUser" resultType="user">select * from user;</select>
</mapper>
5.测试
public class MyTest {@Testpublic void test1() throws IOException {String resources = "mybatis-config.xml";InputStream in = Resources.getResourceAsStream(resources);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);SqlSession sqlSession = sessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.selectUser();for (User user : list) {System.out.println(user);}
sqlSession.close();}
}
12.2.MyBatis-Spring学习
引入Spring之前需要了解mybatis-spring包中的一些重要类;
mybatis-spring –
什么是 MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。
知识基础
在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要
MyBatis-Spring 需要以下版本:
MyBatis-Spring | MyBatis | Spring 框架 | Spring Batch | Java |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.2</version>
</dependency>
要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。
在 MyBatis-Spring 中,可使用SqlSessionFactoryBean来创建 SqlSessionFactory。要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" />
</bean>
注意:SqlSessionFactory需要一个 DataSource(数据源)。这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。
在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。
在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。
SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。
一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings> 或 < typeAliases>元素。
需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(<environments>),数据源(<DataSource>)和 MyBatis 的事务管理器(<transactionManager>)都会被忽略。SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。
SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。
模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。
可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"><constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {this.sqlSession = sqlSession;}
public User getUser(String userId) {return sqlSession.getMapper...;}
}
按下面这样,注入 SqlSessionTemplate:
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl"><property name="sqlSession" ref="sqlSession" />
</bean>
整合实现一
1、引入Spring配置文件beans.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
2、配置数据源替换mybaits的数据源
<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的,我们这里使用spring提供的JDBC--><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="root"/></bean>
3、配置SqlSessionFactory,关联MyBatis
<!--sqlSessionFactory--><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><!--关联MyBatis ,绑定Mybatis配置文件--><property name="configLocation" value="classpath:mybatis-config.xml"/><property name="mapperLocations" value="classpath:com/lxyk/mapper/*.xml"/></bean>
4、注册sqlSessionTemplate,关联sqlSessionFactory;
<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"><!--利用构造器注入--><constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
5、增加Dao接口的实现类;私有化sqlSessionTemplate
public class UserDaoImpl implements UserMapper {
//sqlSession不用我们自己创建了,Spring来管理private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {this.sqlSession = sqlSession;}
public List<User> selectUser() {UserMapper mapper = sqlSession.getMapper(UserMapper.class);return mapper.selectUser();}}
6、注册bean实现
<bean id="userMapper" class="com.lxyk.mapper.UserMapperImpl"><property name="sqlSession" ref="sqlSession"/></bean>
7、测试
public class MyTest {@Testpublic void test1() throws IOException {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserMapper userMapper = context.getBean("userMapper",UserMapper.class);
for (User user : userMapper.selectUser()) {System.out.println(user);}}
}
结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><typeAliases><package name="com.lxyk.pojo"/></typeAliases>
</configuration>
整合实现二
mybatis-spring1.2.3版以上的才有这个 .
官方文档截图 :
dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看
测试:
1、将我们上面写的UserDaoImpl修改一下
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
public List<User> selectUser() {SqlSession sqlSession = getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);return mapper.selectUser();
//return getSqlSession().getMapper(UserMapper.class).selectUser();}
}
2、修改bean的配置
<bean id="userMapper2" class="com.lxyk.mapper.UserMapperImpl2"><property name="sqlSessionFactory" ref="sqlSessionFactory"/></bean>
3、测试
public class MyTest {@Testpublic void test1() throws IOException {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserMapper userMapper = context.getBean("userMapper2",UserMapper.class);
for (User user : userMapper.selectUser()) {System.out.println(user);}}
}
13.声明式事务
回顾事务
把一组业务当成一个业务来做;要么都成功,要么都失败!
事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
事务四个属性ACID原则:
原子性(atomicity)
事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
一致性(consistency)
一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
隔离性(isolation)
可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
持久性(durability)
事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中
测试
将上面的代码拷贝到一个新项目中
在之前的案例中,我们给userDao接口新增两个方法,删除和增加用户;
//添加一个用户
int addUser(User user);//根据id删除用户
int deleteUser(int id);
mapper文件,我们故意把 deletes 写错,测试!
<insert id="addUser" parameterType="user">insert into user (id,name,pwd) values (#{id},#{name},#{pwd})</insert>
<delete id="deleteUser" parameterType="int">deletes from user where id = #{id}</delete>
编写接口的实现类,在实现类中,我们去操作一波
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
public List<User> selectUser() {User user = new User(5,"虾米","889911");UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);mapper.deleteUser(5);
return mapper.selectUser();}
public int addUser(User user) {return getSqlSession().getMapper(UserMapper.class).addUser(user);}
public int deleteUser(int id) {return getSqlSession().getMapper(UserMapper.class).deleteUser(id);}
}
测试
@Test
public void test2(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserMapper mapper = (UserMapper) context.getBean("userDao");List<User> user = mapper.selectUser();System.out.println(user);
}
报错:sql异常,delete写错了
结果 :插入成功!
没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!
以前我们都需要自己手动管理事务,十分麻烦!
但是Spring给我们提供了事务管理,我们只需要配置即可;
Spring中的事务管理
Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。
编程式事务管理
将事务管理代码嵌到业务方法中来控制事务的提交和回滚
缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码
声明式事务管理
一般情况下比编程式事务好用。
将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。
使用Spring管理事务,注意头文件的约束导入 : tx
xmlns:tx="http://www.springframework.org/schema/tx"http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
事务管理器
无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。
就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。
JDBC事务
<!--配置声明式事务--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!--结合AOP实现事务的织入--><!--配置事务通知--><tx:advice id="txAdvice" transaction-manager="transactionManager"><!--配置哪些方法使用什么样的事务,配置事务的传播特性--><!--配置事务的传播特性:new propagation = "REQUIRED" 默认--><tx:attributes><tx:method name="add" propagation="REQUIRED"/><tx:method name="delete" propagation="REQUIRED"/><tx:method name="update" propagation="REQUIRED"/><!--read-only="true" 只读的意思--><tx:method name="query" read-only="true"/><tx:method name="*" propagation="REQUIRED"/></tx:attributes></tx:advice>
spring事务传播特性:
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。
就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!
配置AOP
导入aop的头文件!
<!--配置aop织入事务--><aop:config><!--切入点--><aop:pointcut id="txPointCut" expression="execution(* com.lxyk.mapper.*.*(..))"/><!--切入--><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/></aop:config>
进行测试
删掉刚才插入的数据,再次测试!
public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserMapper userMapper = context.getBean("userMapper", UserMapper.class);for (User user : userMapper.selectUser()) {System.out.println(user);}}
}
思考问题?
为什么需要配置事务?
如果不配置,可能存在数据提交不一致的情况下,
如果我们不在spring中去配置事务,我们就需要在代码中手动配置事务;
事务在项目开发过程非常重要,涉及到数据的一致性和完整性的问题,不容马虎!
系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 系列文章目录
- 前言
- 一、pandas是什么?
- 二、使用步骤
- 1.引入库
- 2.读入数据
- 总结
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、pandas是什么?
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
二、使用步骤
1.引入库
代码如下(示例):
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarnings('ignore') import ssl ssl._create_default_https_context = ssl._create_unverified_context
2.读入数据
代码如下(示例):
data = pd.read_csv( 'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv') print(data.head())
该处使用的url网络请求的数据。
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
冯宝宝说:Spring相关推荐
- 冯宝宝说:SpringMVC
SpringMVC 1.回顾MVC 1.1.什么是MVC MVC是模型(Model).视图(View).控制器(Controller)的简写,是一种软件设计规范. 是将业务逻辑.数据.显示分离的方法来 ...
- 冯宝宝说:mybatis
1.简介 1.1. 什么是mybatis MyBatis 是一款优秀的持久层框架 它支持自定义 SQL.存储过程以及高级映射. MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集 ...
- Spring中的5种Aop常见应用方式(扫描注解方式)
转载于:https://zhuanlan.zhihu.com/p/103236714 提到Aop,不得不提的那就是动态代理:关于动态代理,可以参考前面写过的文章 加耀:浅谈动态代理zhuanlan. ...
- 商家酒店模块之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c
源码地址来源: https://minglisoft.cn/honghu2/business.html /*** Copyright © 2012-2017 <a href="http ...
- 这篇 MySQL 索引和 B+Tree 讲的太通俗易懂!
点击上方蓝色"java大数据修炼之道", 选择"设为星标" 每晚九点: 技术干货 ???? 必定奉上 来源:https://blog.csdn.net/b_x_ ...
- Python网络爬虫--urllib
本篇随便记录学习崔庆才老师编著的<Python3 网络爬虫开发实战>以及urllib标准库使用 urllib库是Python内置的HTTP请求库,包含四个模块: request:最基本的H ...
- JavaScript中的面向对象
1.js中的数据类型 原始类型:字符串 string. 数字 number. 布尔 boolean. null undefined .其中null表示没有对象,undefined 表示没有定义 数组 ...
- 加载八叉树索引文件_这篇 MySQL 索引和 B+Tree 讲的太通俗易懂!
正确的创建合适的索引,是提升数据库查询性能的基础.在正式讲解之前,对后面举例中使用的表结构先简单看一下: create table user (id bigint not null comment ' ...
- onnx 测试_ONNX 现场演示教程
ONNX 现场演示教程 译者:冯宝宝 本教程将向您展示如何使用ONNX将已从PyTorch导出的神经模型传输模型转换为Apple CoreML格式.这将允许您在Apple设备上轻松运行深度学习模型,在 ...
最新文章
- 代码之美——Doom3源代码赏析2
- mysql1193 HY000_[MySQL参数取值] Status取值ERROR 1193 (HY000): Unknown system_MySQL
- 构建高可用服务器之 Keepalive参数详解
- Windows下安装配置SubVersion的简明步骤
- MNIST机器学习入门(二)
- Javascript第五章location对象第五课
- 2022最新开源分销商城小程序源码系统前端+后端+搭建教程
- n1盒子救砖_斐讯N1盒子刷机救砖教程
- 中国青年报:“宋祖德现象”考验道德与法
- Alien Skin ExposureX8专业图像编辑器全新版本
- 清北学堂18年国庆刷题班游记
- 微信H5页面源码如何看
- php date.getday,JavaScript从Date对象返回一周中的某一天 (0 ~ 6)的方法getDay()
- 本地颁发 SSL 证书,并开启 https 服务调试
- 谷歌adwords教程_区块链技术作为Google AdWords中的安全剧院
- solidworks显示无法连接到服务器,SOLIDWORKS Electrical解决方法:无法连接协同服务器...
- Crosslink-NX器件应用案例(2): MIPI的多源合成(MUX)与分发(DeMUX)
- 软件生命周期管理系统ALM配置说明(二)
- C++实现K-means,聚类原理解析(并用在图片像素点聚类)
- Java中对索引的知识总结
热门文章
- 学生用计算机怎么计算sin1,sin的数值哪来的
- 图片音视频应用开发的工具与服务大礼包
- ShareSDK for Android 版本:V 2.5.0发布
- 如何去掉WPS中.docx文档灰色的中括号?
- 台式电脑用网线可以上网,为什么把网线插到笔记本电脑上就连不上网的问题的解决
- teleport ultra整站下载工具
- 当写烂代码的人离职之后....
- leetcode:1488. 避免洪水泛滥【二分 + 贪心】
- MapReduce的经典入门案例
- 中级软件设计师2011上半年上午试题