spring
文章目录:
- 1.spring简介
- 2.IOC推导原型及本质
- 3.第一个spring程序
- 4.IOC创建对象的方式
- 5.spring配置说明
- 6.Dl依赖注入环境(依赖注入的方式)
- 7.bean的作用域
- 8.自动装配bean
- 9.用注解实现自动装配
- 10.spring使用注解开发
- 11.使用java的方式配置spring
- 12 代理模式
- 13 使用spring实现AOP
- 14.整合mybatis
- 15.Spring声明式事务
1.spring简介
spring简介
之后要导入的一些maven依赖
spring的优点
spring组成
2.IOC推导原型及本质
推导原型
传统的分层模式架构
在业务层,主要调动dao层的接口实现,
测试类
IOC本质
3.第一个spring程序
1.导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
</dependencies>
2.创建实体类
package com.wash.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
3.注入bean对象
在resources文件夹下创建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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="com.wash.pojo.Hello">
<property name="str" value="spring"/>
</bean>
</beans>
4.在测试类中获取bean对象
import com.wash.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest {
public static void main(String[] args) {
ApplicationContext context =new ClassPathXmlApplicationContext("beans.xml");
Hello hello= (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
之后我们就可以通过这种方式来改变,之前在service层的接口实现方法创建对象,现在直接配置xml文件即可
4.IOC创建对象的方式
1.使用无参构造创建对象,默认
2.使用有参构造创建对象
- 3.直接通过参数名来设置
注意:在配置文件加载的时候,spring容器中管理的对象就已经初始化了。
5.spring配置说明
6.Dl依赖注入环境
依赖注入实际是Set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
第一种:普通值注入,value
<bean id="hello" class="com.wash.pojo.Hello">
<property name="str" value="spring"/>
</bean>
依赖注入的方式:
1.构造器注入:
2.Set方式注入[重点]
<bean id="a" class="com.wash.pojo.Hello1"/>
<bean id="hello" class="com.wash.pojo.Hello">
<property name="str" value="spring"/>
<property name="adderess" ref="a"/>
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>三国</value>
</array>
</property>
<property name="hobbys">
<list>
<value>听歌</value>
<value>敲代码</value>
</list>
</property>
<property name="card">
<map>
<entry key="身份证号" value="22134565432"/>
<entry key="银行卡" value="23455643121"/>
</map>
</property>
<property name="games">
<set>
<value>lol</value>
<value>cf</value>
</set>
</property>
<property name="wife">
<null/>
</property>
<property name="info">
<props>
<prop key="学号">1706010122</prop>
<prop key="性别">男</prop>
<prop key="password">19990329</prop>
</props>
</property>
</bean>
3.其他方式注入
- c命名空间注入
7.bean的作用域
1.单例模式(spring默认机制)
<bean id="hhe" class="com.wash.pojo.Hello" c:str="shuaishuai" scope="singleton"/>
2.原型模式:每次从容器中bean的时候,都会产生一个新对象
<bean id="hhe" class="com.wash.pojo.Hello" c:str="shuaishuai" scope="prototype"/>
3.其他的request,session,application,这些只能在web开发中使用到。
8.自动装配bean
- 自动装配是spring满足bean依赖的一种方式
- spring会在上下文中自动寻找,并自动给bean装配属性
在spring中有三种装配的方式
1.在xml显示的配置
2.在java中显示配置
3.隐式的自动装配bean(重点)
9.用注解实现自动装配
jdk1.5支持的注解,spring2.5就支持注解了。
导入约束
<context-annotation-config/>
-
@Autowired,直接在实体类的属性上使用即可,也可以在set方法上使用,也可以忽视set方法使用
省略set方法(使用Autowired我们就可以不用编写set方法了,前提是这个自动装配的属性在IOC容器中存在,且符合名字byname)
-
@如果显示定义了Autowired的required属性为false,说明这个对象为null,否则不允许为空。
@Autowired(requested=false)
-
@Nullable 字段标记了这个注解,说明这个字段可以为null。
-
@Qualifier(value="")
-
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入。
-
@resource或@resource(name=“bean的id名”)
10.spring使用注解开发
在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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
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:annotation-config/>
<context:component-scan base-package="com.wash.pojo"/>
1.bean
通过@Component:组件,放在类上,说明这个类被spring管理了,就是bean。
2属性如何注入
package com.wash.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class User {
@Value("shuai")
public String name;
}
3.衍生的注解
@Component有几个衍生注解,在web开发中,会按照mvc三层架构分层
- dao 【@Repository】
- service 【@Service】
- controller 【@Controller】
- 这四个注解功能是一样的,都是代表将某个类注册到spring中,装配bean
4.自动装配
5.作用域
6.xml与注解
- xml更加万能,适用于任何场合,维护简单方便
- 注解不是自己的类使用不了,维护相对复杂
xml与注解的最佳实践
- xml用来管理bean
- 注解只负责完成属性的注入
- 在使用的过程中只需要注意一个问题,必须让注解生效,就需要开启注解的支持
<context:component-scan base-package="com.wash"/>
<context:annotation-config/>
11使用java的方式配置spring
现在完全不使用spring的xml配置了,全权交给Java来做
javaConfig 是spring的一个子项目,在spring4之后,它成为了一个核心功能。
package com.wash.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class User {
public String name;
public String getName() {
return name;
}
@Value("shuai")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
<?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.wash.pojo"/>
<context:annotation-config/>
package com.wash.config;
import com.wash.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.wash.pojo")
public class Config {
@Bean
public User user(){
return new User();
}
}
import com.wash.config.Config;
import com.wash.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test03 {
@Test
public static void main(String[] args) {
ApplicationContext context= new AnnotationConfigApplicationContext(Config.class);
User get =(User)context.getBean("user");
System.out.println(get.getName());
}
}
12.代理模式
为什么要学习代理模式?
12-1 静态代理
public interface Rent {
public void rent();
}
public class Host implements Rent{
public void rent() {
System.out.println("房东要租房子");
}
}
public class proxy implements Rent{
private Host host;
public proxy(){}
public proxy(Host host) {
this.host = host;
}
public void rent() {
host.rent();
seehouse();
hetong();
fare();
}
public void seehouse(){
System.out.println("中介带你看房");
}
public void hetong(){
System.out.println("签租赁合同");
}
public void fare(){
System.out.println("收中介费");
}
}
public class client {
public static void main(String[] args) {
Host host = new Host();
proxy p=new proxy(host);
p.rent();
}
}
12-2 动态代理
public class ProxyInvocationHandler implements InvocationHandler {
private Object rent;
public void setRent(Object rent) {
this.rent = rent;
}
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result= method.invoke(rent, args);
return result;
}
}
public class client {
public static void main(String[] args) {
serviceimpl service=new serviceimpl();
ProxyInvocationHandler pih= new ProxyInvocationHandler();
pih.setRent(service);
Service proxy=(Service) pih.getProxy();
proxy.add1();
}
}
一个动态代理类可以代理多个类,只要实现同一个接口即可
13.使用spring实现AOP
- 方法1:使用spring的API接口【主要springAPI接口实现】
- 方法2:自定义来实现AOP【主要是切面定义】
- 方法3:注解实现AOP
使用AOP需要导入一个依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式1:使用spring的API接口 【主要springAPI接口实现】
package com.wash.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
public class impl 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("已查询");
}
}
package com.wash.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
package com.wash.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class Afterlog implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"返回结果为"+o);
}
}
<?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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="UserService" class="com.wash.service.impl"/>
<bean id="log" class="com.wash.log.Log"/>
<bean id="afterlog" class="com.wash.log.Afterlog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.wash.service.impl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
</beans>
import com.wash.service.UserService;
import com.wash.service.impl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.naming.CompositeName;
public class Test001 {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationComtext.xml");
UserService userService=(UserService)context.getBean("UserService") ;
userService.query();
}
}
方法2:自定义来实现AOP【主要是切面定义】
package com.wash.log;
public class DivLog {
public void before(){
System.out.println("=======方法执行前======");
}
public void after(){
System.out.println("=======方法执行后=======");
}
}
-
<bean id="diy" class="com.wash.log.DivLog"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="point" expression="execution(* com.wash.service.impl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方法3:注解方式实现AOP
package com.wash.log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
@org.aspectj.lang.annotation.Aspect
public class Aspect {
@Before("execution(* com.wash.service.impl.*(..))")
public void before1(){
System.out.println("=======方法执行前======");
}
@After("execution(* com.wash.service.impl.*(..))")
public void after1(){
System.out.println("=======方法执行后=======");
}
@Around("execution(* com.wash.service.impl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Object proceed= jp.proceed();
System.out.println("环绕后");
Signature signature= jp.getSignature();
System.out.println("signature"+signature);
}
}
<bean id="aspect" class="com.wash.log.Aspect"/>
<aop:aspectj-autoproxy/>
14.整合mybatis(两种方法)
步骤:
1.导入相关jar包
- junit
- mybatis
- mysql数据库
- spring相关包
- aop织入
- mybatis-spring
maven依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
2.编写配置文件
3.测试
mybatis的步骤
package com.wash.pojo;
import lombok.Data;
@Data
public class User {
private int id;
private String ename;
private String pwd;
}
//2.编写核心配置文件
<?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>
<typeAliases>
<package name="com.wash.pojo"/>
</typeAliases>
<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/mybatis?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="xiangying216"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.wash.mapper.Usermapper"/>
</mappers>
</configuration>
package com.wash.mapper;
import com.wash.pojo.User;
import java.util.List;
public interface Usermapper {
public List<User>selectUser();
}
//4.编写mapper.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">
<mapper namespace="com.wash.mapper.Usermapper">
<select id="selectUser" resultType="user">
select * from mybatis.user
</select>
</mapper>
import com.wash.mapper.Usermapper;
import com.wash.pojo.User;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Mytest {
@Test
public void test() throws IOException {
String resources="config.xml";
InputStream inputStream= Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession=sqlSessionFactory.openSession(true);
Usermapper usermapper=sqlSession.getMapper(Usermapper.class);
List<User> userList=usermapper.selectUser();
for (User user : userList) {
System.out.println(user);
}
}
}
注意:当识别不出mapper.xml时,在pom.xml中添加
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
整合步骤:
- 编写数据源配置
- 配置sqlSessionFactory
- 配置sqlSessionTemplate
- 需要给接口加实现类
- 将写的实现类注入到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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<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?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="xiangying216"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="configLocation" value="classpath:config.xml"/>
<property name="mapperLocations" value="classpath:com/wash/mapper/*.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.wash.mapper.UserMapperimpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
package com.wash.mapper;
import com.wash.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperimpl implements Usermapper{
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
Usermapper mapper=sqlSession.getMapper(Usermapper.class);
return mapper.selectUser();
}
}
<bean id="userMapper" class="com.wash.mapper.UserMapperimpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
import com.wash.mapper.Usermapper;
import com.wash.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest1 {
@Test
public void test(){
ApplicationContext context= new ClassPathXmlApplicationContext("spring-dao.xml");
Usermapper usermapper= context.getBean("userMapper", Usermapper.class);
for (User user : usermapper.selectUser()) {
System.out.println(user);
}
}
}
注:也可以在applicationcontext.xml导入此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">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.wash.mapper.UserMapperimpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
整合mybatis方法2
在方法一的基础上把接口实现类改掉
import com.wash.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperimpl2 extends SqlSessionDaoSupport implements Usermapper{
public List<User> selectUser() {
return getSqlSession().getMapper(Usermapper.class).selectUser();
}
}
之后在xml中将实现类注入spring,并且属性值不是sqlSession而是sqlSessionFactory
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">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.wash.mapper.UserMapperimpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
<bean id="usermapper2" class="com.wash.mapper.UserMapperimpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans
15.Spring声明式事务
事务
spring中的事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中,进行事务的管理
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="datasource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txpointcut" expression="execution(* com.wash.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
</aop:config>
到这里spring整理完了,之后会整理springMVC的内容
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)