Mybatis
优点:
1、简单易学灵活,使用的人多。
2、提供xml标签,支持编写动态sql,且sql和代码分离,提高了可维护性。
3、提供映射标签,支持对象关系组件维护。
第一个mybatis程序:
1、创建一个普通的maven项目。
2、导入依赖,创建mybatis必须要有mysql、mybatis、和junit三个依赖
<?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>cn.zjh</groupId>
<artifactId>mybatis01</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--在build中配置resources来防止资源过滤问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
3、编写mybatis的核心配置文件,xml配置文件中包含了对mybatis系统的核心配置文件,包含获取数据库链接实例的数据源和决定事物范围和控制方式的事务管理器。xml文件一般命名为mybatis-config.xml,放在resources目录下。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_book?userSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--每一个mapper.xml都需要在mybatis核心配置文件中声明注册-->
<mappers>
<mapper resource="cn/zjh/mapper/UserMapper.xml" />
</mappers>
</configuration>
4、编写mybatis工具类,工具类的作用就是面向数据库,执行sql语句。,工具类可以单独存放在utils层中。
package cn.zjh.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
//使用mybatis获取sqlsessionfactory对象
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了sqlSessionFactory,我们就可以从中获取sqlsession实例了
//sqlsession完全包含了面向数据库执行sql命令所需的所有方法
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
5、编写数据库实体类,实体类在pojo层。
package cn.zjh.pojo;
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
6、构建mapper层,用来写需求方法。
package cn.zjh.mapper;
import cn.zjh.pojo.User;
import java.util.List;
public interface UserMapper {
//查询全部
List<User> getUserList();
}
7、使用xml来实现需求
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的mapper接口-->
<mapper namespace="cn.zjh.mapper.UserMapper">
<!--resultType是sql语句执行返回值,方法返回什么类型就绑定什么全名类型,resultmap返回集-->
<!--parameterType:参数类型-->
<select id="getUserList" resultType="cn.zjh.pojo.User">
select * from `user`
</select>
</mapper>
8、测试,测试的类最好和需求类包同名,比如需求类在src的java的cn.zjh.mapper包中,那么测试类就要在test的java的cn.zjh.mapper包中。
package cn.zjh.mapper;
import cn.zjh.pojo.User;
import cn.zjh.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UsserMapperTest {
@Test
public void test(){
//1、获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2、获取数据库结果
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//3、使用list集合来接收
List<User> userList = userMapper.getUserList();
//4、遍历集合
for (User user : userList) {
System.out.println(user);
}
//5、关闭sqlsession
sqlSession.close();
}
}
第一个mybatis程序的包目录
测试中的常见错误:
Type interface cn.zjh.mapper.UserMapper is not known to the MapperRegistry. mapper注册中心usermapper接口是未知的, 这个报错是没有在mybatis-config.xml里面声明注册,需要添加注册
<mappers> <mapper resource="cn/zjh/mapper/UserMapper.xml" /> </mappers>
java.lang.ExceptionInInitializerError ### Error building SqlSession. ### The error may exist in cn/zjh/mapper/UserMapper.xml 初始化异常,UserMapper.xml不存在获取不到 是因为maven资源过滤问题,写的配置文件无法导出或者生效, 解决方案:在pom.xml中添加build,在build中配置resources来防止资源到处问题
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
mybatis增删改查
usermapper
//查询全部
List<User> getUserList();
//通过id查询
User getUserId(int id);
//添加(因为添加不需要返回值,所以可以不写返回类型,但是有可能会报错,所以最好给一个int类型)
int InsertUser(User user);
// 修改
int UpdateUser(User user);
// 删除
int DeleteUser(int a);
usermapper.xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的mapper接口-->
<mapper namespace="cn.zjh.mapper.UserMapper">
<select id="getUserList" resultType="cn.zjh.pojo.User">
select * from `user`
</select>
<select id="getUserId" resultType="cn.zjh.pojo.User" parameterType="java.lang.Integer">
select * from `user` where id = #{id}
</select>
<insert id="InsertUser" parameterType="cn.zjh.pojo.User">
insert into `user`(id,`name`,pwd) values(#{id},#{name},#{pwd})
</insert>
<update id="UpdateUser" parameterType="cn.zjh.pojo.User">
update `user` set `name`=#{name},pwd=#{pwd} where id=#{id}
</update>
<delete id="DeleteUser" parameterType="java.lang.Integer">
delete from `user` where id=#{id}
</delete>
</mapper>
test
//查询全部
@Test
public void test1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
//通过id查询
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User userId = mapper.getUserId(1);
System.out.println(userId);
}
// 增删改需要提交事务才能让数据库的数据发生变化
//添加
@Test
public void test3(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 因为添加不需要返回值,所以可以不用给一个集合来接收数据
// 因为方法的参数是一个引用类型,所以需要通过new一个来给一个参数,
// 如果参数不需要id的话,就需要给user定义一个没有id的有参构造
// 当然也可以定义一个a来判断有没有添加成功,成功了的话a就大于0
int a = mapper.InsertUser(new User("张三","123456"));
if(a>0){
System.out.println("添加成功");
}
// 提交事物
sqlSession.commit();
// 关闭流
sqlSession.close();
}
// 修改
@Test
public void test4(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.UpdateUser(new User(3, "王五"));
if(i>0){
System.out.println("修改成功");
}
sqlSession.commit();
sqlSession.close();
}
// 删除
@Test
public void test5(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.DeleteUser(3);
if(i>0){
System.out.println("删除成功");
}
sqlSession.commit();
sqlSession.close();
}
map参数
在对增删改的操作上,都是对很少的字段进行操作的,在企业中,对于字段的更改往往都有很多,所以对于增删改的参数可以使用map来,这样可以更加的方便。使用map有一个缺点,需要全部修改
// 使用map的方法进行多个字段的修改
int UpdateUser2(Map<String,Object> map);
这样的方法可以不用一直添加不同的构造方法, 从而达到想修改什么字段就往map添加什么字段
<!--这样的方法可以不用一直添加不同的构造方法,
从而达到想修改什么字段就往map添加什么字段-->
<update id="UpdateUser2" parameterType="java.util.Map">
update `user` set `name`=#{name},pwd=#{pwd} where id=#{id}
</update>
测试
@Test
public void test6(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("id",2);
map.put("name","王五");
map.put("pwd","123");
int i = mapper.UpdateUser2(map);
if(i>0){
System.out.println("修改成功");
}
sqlSession.commit();
sqlSession.close();
}
使用map作为参数不仅可以用在增删改的操作上,对于查也是可以的,比如查询的时候不仅有id条件,还有很多其他字段的条件,这个时候就可以使用map作为参数类型。
模糊查询
通过名字模糊查询
// 通过名字模糊查询
List<User> getUserlike(String a);
第一种方法,在java代码执行的时候,传递通配符%%,更加的安全,不会出错
<select id="getUserlike" resultType="cn.zjh.pojo.User" parameterType="java.lang.String">
select * from `user` where `name` like #{value}
</select>
@Test
public void test7(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userlike = mapper.getUserlike("%张%");
for (User user : userlike) {
System.out.println(user);
}
}
第二种方法,在sql凭借中使用通配符,更加方便,但是会有sql注入的问题,有的时候会报错。
<select id="getUserlike" resultType="cn.zjh.pojo.User" parameterType="java.lang.String">
select * from `user` where `name` like "%"#{value}"%"
</select>
@Test
public void test7(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userlike = mapper.getUserlike("张");
for (User user : userlike) {
System.out.println(user);
}
}
配置解析
引入外部配置文件:
1、编写一个db.properties配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_book?userSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
2、在核心配置文件中引入,properties属性必须放在最前面,格式规范
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn/zjh/mapper/UserMapper.xml" />
</mappers>
</configuration>
类型别名
可以给实体类起别名,这样可以不用写全限定名直接用别名
在xml配置文件中配置typealiases,放置注意顺序,xml对于这些属性的位置严格规范。
两种方式,第一种指定实体类别名
<typeAliases>
<typeAlias type="cn.zjh.pojo.User" alias="user"></typeAlias>
</typeAliases>
第二种方式,指定包下的实体类的类名都为别名,首字母不区分大小写
<typeAliases>
<package name="cn.zjh.pojo"></package>
</typeAliases>
在实体类较少的时候使用第一种方式,实体类较多的时候使用第二种
第一种可以随意设置别名,第二种只能是类名,如果非要改,需要在实体类中增加注解
@Alias("别名")
结果集映射:
resultmap元素是mybatis中最重要最强大的元素,对于简单的语句不需要配置结果映射,对于复杂的语句只需要描述他们的关系就行了。
在实体类的属性和字段名不一样的时候,要让对于数据库的操作起到作用只能给属性起别名,但是这种方法太过笨重,所以就可以使用resultmap来配置映射
private int id;
private String name;
private String password;
当属性的password和数据库pwd不一样的时候,运行会报错
只需要在mapper.xml中添加resultmap属性去声明
<!--结果集映射,id就是下面resultmap的名字,type就是需要更改的实体类-->
<resultMap id="UserMap" type="User">
<!--column数据库中的字段,property实体类中的属性-->
<result column="pwd" property="password"></result>
</resultMap>
<!--使用resultmap要把resulttype改成resultmap,并且给其一个值相当于名字-->
<select id="getUserList" resultMap="UserMap">
select * from `user`
</select>
Spring
spring,翻译成中文,意思就是春天,万物复苏的季节,spring框架,在代码行业的影响力也是有春天这个意思,spring框架是由于软件开发的复杂性而创建的,目的就是解决企业开发的复杂性,spring的理念就是使现有的技术更加容易使用。spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架。
第一个Spring程序
使用spring需要先导入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
实体类的编写,必须要有属性的set方法,因为控制反转的本质实际上是通过set注入的
public class User {
private String name;
private String pwd;
public User() {
}
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
编辑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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用spring来创建对象,在spring中这些都称为bean-->
<!--id是给对象一个名字,class是用哪个实体类来创建对象相当于new-->
<bean id="user" class="cn.zjh.pojo.User">
<!--property指向那个属性,value赋值基本数值,ref引用bean对象引用值-->
<property name="name" value="张家豪"/>
<property name="pwd" value="123"/>
</bean>
</beans>
测试
public class UserTest {
public static void main(String[] args) {
// 获取spring中的bean对象
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// getbean取出对象
User user = (User) context.getBean("user");
// 输出
System.out.println(user.toString());
}
}
ioc创建对象的方式:
平常的创建方式都是在实体类有无参构造的时候创建的,当需要使用有参构造创建的时候有三种方法
第一种,下标赋值,通过有参构造的参数来赋值
<bean id="user" class="cn.zjh.pojo.User">
<constructor-arg index="0" value="张家豪"/>
</bean>
第二种,类型赋值,通过参数的类型来赋值,不推荐,因为参数类型有可能重复冲突
<bean id="user" class="cn.zjh.pojo.User">
<constructor-arg type="java.lang.String" value="张家豪"/>
</bean>
第三种,通过参数名称来赋值,常用
<constructor-arg name="name" value="张家豪"/>
在容器中的bean对象,只要在配置文件加载的时候,就已经被初始化了
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
也就是这一段话运行的时候,所有的bean对象都已经被加载,所有的对象的无参构造都会执行。
别名
在给bean对象取名字的时候通常使用id来给他一个唯一标识符,在让调用这个bean对象的时候通过id来调用,但是还可以通过别名来调用
1、alias别名用法
给bean对象取一个id,然后在bean结束后面添加别名
<alias name="bean对象id" alias="别名"/>
2、name别名用法,name的用法很强大,既可以用逗号分开别名,也可以用空格分号等等
<bean id="user" class="" name="别名1,别名2"/>
import
一般用于团队开发使用,可以将多个配置文件导入到一起合并为一个,遇到相同的bean对象会和为一个
<import resource="其他xml名称"/>;
依赖注入set注入其他类型属性
<!--第一种,常见值注入-->
<property name="name" value="张家豪"/>
<!--第二种,引用类型注入-->
<property name="name" ref="user"/>
<!--第三种,数组注入-->
<property name="name">
<array>
<value>红楼梦</value>
<array>水浒传</array>
</array>
</property>
<!--第四种,list注入-->
<property name="name">
<list>
<value>敲代码</value>
</list>
</property>
<!--第五种,map注入-->
<property name="name">
<map>
<entry key="" value=""/>
</map>
</property>
<!--空字符串-->
<property name="name" value=""/>
<!--null-->
<property name="name">
<null/>
</property>
扩展注入:p和c注入
p命名空间注入,需要添加p约束,可以直接在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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="user" class="cn.zjh.pojo.User" p:name="张家豪"/>
</beans>
c命名注入,通过有参构造注入,直接在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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="user2" class="cn.zjh.pojo.User" c:name="张家豪"/>
</beans>
bean的作用域
1、单例模式,spring的默认模式,意思是在加载完spring配置文件之后
不管你getBean多少次这个bean对象,这个对象都只还是这个同样的对象
<bean id="name" class="" scope="singleton"/>
2、原型模式,每次getBean都是一个新的对象
<bean id="name" class="" scope="prototype"/>
3、其余的request、session、application都是在web开发的时候使用的
request每次请求都是一个新对象
session在作用域里面是一同个对象
application在全局里面都是同一个
自动装配:
在注入引用类型的值的时候,通常我们需要ref这一步才能输出
<bean id="people" class="cn.zjh.pojo.people">
<property name="name" value="张家豪"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
spring给我们提供了自动装配功能,autowire属性
1、byname
1、byname自动装配
<bean id="cat" class="cn.zjh.pojo.Cat"/>
<bean id="dog" class="cn.zjh.pojo.Dog"/>
<bean id="people" class="cn.zjh.pojo.people" autowire="byName">
<property name="name" value="张家豪"/>
</bean>
byname表示通过bean对象的属性自动寻找到在容器中bean对象的id,当属性和id一样的时候就自动装配,不一样就会报错失败。
2、bytype
2、bytype自动装配
<bean id="cat" class="cn.zjh.pojo.Cat"/>
<bean id="dog" class="cn.zjh.pojo.Dog"/>
<bean id="dog2" class="cn.zjh.pojo.Dog"/>
<bean id="people" class="cn.zjh.pojo.people" autowire="byType">
<property name="name" value="张家豪"/>
</bean>
bytype是通过类型来自动装配的,但是像上诉代码一样。有两个dog类型就会自动装配失败,bytype使用的时候也可以让dog对象省略id,也可以成功装配,根据类型装配
3、注解装配
当然,这样的代码还不是最简单的,使用注解实现自动装配可以让代码更加简单
使用注解代码需要导入约束并且配置注解的支持
<?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="cn.zjh.pojo"/>
<!--导入约束配置注解注入的支持-->
<context:annotation-config/>
<bean id="cat" class="cn.zjh.pojo.Cat"/>
<bean id="dog" class="cn.zjh.pojo.Dog"/>
<bean id="people" class="cn.zjh.pojo.people">
<property name="name" value="张家豪"/>
</bean>
</beans>
1、@autowired
这样就可以直接在实体类的引用数据类型上面添加自动装配标志了
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
如果显示定义了autowired的required值为false,则表示这个对象可以为null
@Autowired(required=false
@autowired是默认按照bytype类型注入的,如果出现了多个bean对象类型相同,使用@Autowired会报错,但是可以在添加一个@qualifer(“bean对象id”)来声明@Autowired自动装配的是哪一个。
@Autowired
@Qualifier("dog")
private Dog dog;
两者配套使用
2、@resource
还有个混合功能的自动装配@Resource,当bean对象有多个类型一样的且标注了这个自动装配的时候,他会先通过bean对象的id来判断自动装配哪一个,会先装配id和属性名一样的
@Resource
private Cat cat;
当所有的id都不一样的时候也可以声明一下自动装配哪一个bean对象
@Resource(name = "cat")
private Cat cat;
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"
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
">
<!--声明在pojo包下的注解可以生效-->
<context:component-scan base-package="cn.zjh.pojo"/>
<!--配置注解注入的支持-->
<context:annotation-config/>
</beans>
1、@component
等价于<bean id="类小写" class="类路径名"/> 这个注解放在实体类的最上面就说明这个类被spring管理了,就是被bean了。
@Component
public class User {
public String name="张家豪";
}
2、value
@component的衍生注解,给属性赋值
@Component
public class User {
@Value("张家豪")
public String name;
}
还有几个衍生注解,在web开发中,会按照mvc三层架构分层
1、dao层即mapper层使用注解:@Repository
2、service层使用注解:@Service
3、controller层使用注解:@Controller
4、pojo层就是现在用的注解:@Component
这四个注解的功能都是一样的,都是代表将某个类注册到spring中,相当于bean 在web开发下就不能只声明pojo包下的注解生效了,要全部一起注解
<!--声明在zjh包下的所有注解可以生效-->
<context:component-scan base-package="cn.zjh"/>
AOP
面向切面,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
通俗描述就是不通过修改源代码的方式,在主干功能里面添加新功能。
比如一段执行程序是登入---访问数据库比对账号密码----密码正确跳到主页,切面的意思就是在登入的时候添加一个判断登入是用户登入还是管理员登入,以前需要在登入这个源代码里面添加if--eles判断也就是修改了源代码,切面的操作是通过动态代理模式的方式来从新写一段代码来代理登入这个操作,源代码不变。
AOP有几个常用的术语
1、连接点 类里面哪些方法可以增强,这些方法称为连接点
2、切入点 实际被增强的方法叫做切入点
3、通知(日志) 增强操作的逻辑代码叫做通知,通知有五种类型
前置通知:MethodBeforeAdvice
后置通知:AfterReturningAdvice
环绕通知:MethodInterceptor
异常通知:ThrowsAdviece
最终通知:不管执行成功失败都会在最后的增强,像fainlly
4、 切面 把通知应用到切入点的过程
spring框架一般都是基于Aspectj实现AOP操作,Aspectj不是spring组成部分,是独立的框架,一般都是和spring框架一起使用进行AOP操作。有两种方法可以实现AOP,一种是基于xml配置文件实现,还有一种是基于注解方式实现。
实现AOP
相关依赖:
spring使用AOP需要导入依赖
<!--spring支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<!-- spring aop支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<!--AspectJ支持-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.bundles</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8_2</version>
</dependency>
定义一个接口
package cn.zjh.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
实现接口:
package cn.zjh.service;
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("查询了一个用户");
}
}
springAOP实现方式一,使用spring API接口:
编写一个前置通知
package cn.zjh.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
//通过继承这个通知类来实现AOP,MethodBeforeAdvice:前置通知
public class Log implements MethodBeforeAdvice {
// method:要执行的目标对象的方法
// args: 参数
// target:目标对象
public void before(Method method, Object[] args, Object target) throws Throwable {
// target.getClass().getName()获取该对象的全路径名,method.getName()获取该方法名
System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
}
注:
如果需要编写其他类型通知只需要把继承类换一下
前置通知:MethodBeforeAdvice
后置通知:AfterReturningAdvice
环绕通知:MethodInterceptor
异常通知:ThrowsAdviece
编写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
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userservice" class="cn.zjh.service.UserServiceImpl"/>
<bean id="log" class="cn.zjh.log.Log"/>
<!--使用aop需要导入约束-->
<aop:config>
<!--设置切入点,expression:表达式-->
<aop:pointcut id="user" expression="execution(* cn.zjh.service.UserServiceImpl.*(..))"/>
<!--执行前置通知-->
<!--advice:前置通知id,pointcut:切入点id-->
<aop:advisor advice-ref="log" pointcut-ref="user"/>
</aop:config>
</beans>
表达式的语法结构:
execution([权限修饰符]{返回类型}{类全路径}{方法名称}{参数列表})
权限修饰符可以使用*代替,表示所有权限修饰符 返回类型可以用空格表示,参数可以使用..代替
举例一:对cn.zjh.pojo.user类里面的add方法增强 execution(* cn.zjh.pojo.user.add(..))
举例二:对cn.zjh.pojo.user类里面所有的方法增强 execution(* cn.zjh.pojo.user.*(..))
举例三:对pojo包下所有的类,类里所有的方法进行增强 execution(* cn.zjh.pojo.*.*(..))
测试:
import cn.zjh.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 动态代理的是接口,所以一定要转换为接口类型
UserService userservice = (UserService) context.getBean("userservice");
userservice.add();
}
}
方式二:自定义类:
自定义类
package cn.zjh.log;
public class test {
public void qian(){
System.out.println("======方法执行之前======");
}
public void hou(){
System.out.println("======方法执行之后======");
}
}
xml配置
<bean id="userservice" class="cn.zjh.service.UserServiceImpl"/>
<bean id="test" class="cn.zjh.log.test"/>
<!--ref是指向要切入进来的类,就是作为通知的类-->
<aop:config>
<aop:aspect ref="test">
<!--定义切入点-->
<aop:pointcut id="user" expression="execution(* cn.zjh.service.UserServiceImpl.*(..))"/>
<!--设置通知的形式,-->
<!--method表示需要设置通知的方法名,pointcut-ref表示切入点在哪里-->
<!--前置通知-->
<aop:before method="qian" pointcut-ref="user"/>
<!--后置通知-->
<aop:after method="hou" pointcut-ref="user"/>
</aop:aspect>
</aop:config>
需要注意,在xml配置中,bean切入点userservice的id和在aop设置切入点的时候的id不能一样,否则会报错。
测试
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 动态代理的是接口,所以一定要转换为接口类型
UserService userservice = (UserService) context.getBean("userservice");
userservice.select();
}
}
方式三,注解实现AOP
使用注解需要先在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
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="test" class="cn.zjh.log.test" />
<bean id="service" class="cn.zjh.service.UserServiceImpl" />
<!--开启注解支持-->
<aop:aspectj-autoproxy />
通知方法,注意注解的包是否正确
package cn.zjh.log;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//标注这个类是一个切面
@Aspect
public class test {
// before给切面一个前置通知,后面是指出切面地址
@Before("execution(* cn.zjh.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前。。。");
}
}
测试
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService user = (UserService) context.getBean("user");
user.add();
}
其他几种通知格式:
1、前置通知 @Before("execution(* cn.zjh.service.UserServiceImpl.*(..))")
2、后置通知 @After("execution(* cn.zjh.service.UserServiceImpl.*(..))")
3、环绕通知,环绕通知不同,需要有个参数 这个参数就是来获取原本代码的执行,且需要抛出异常
@Around("execution(* cn.zjh.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable{
System.out.println("方法执行前。。。");
Object proceed = jp.proceed();
System.out.println("方法执行后");
}