一、建造者模式.
1、MyBatis框架的环境类创建,通过静态内部类实现.
public final class Environment {
// 环境ID
private final String id;
// 事务工厂
private final TransactionFactory transactionFactory;
// 数据源
private final DataSource dataSource;
public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
// 1、构造方法中做一些必要的参数校验
if (id == null) {
throw new IllegalArgumentException("Parameter 'id' must not be null");
}
if (transactionFactory == null) {
throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
}
this.id = id;
if (dataSource == null) {
throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
}
this.transactionFactory = transactionFactory;
this.dataSource = dataSource;
}
// 2、静态内部类实现Builder
public static class Builder {
private String id;
private TransactionFactory transactionFactory;
private DataSource dataSource;
public Builder(String id) {
this.id = id;
}
public Builder transactionFactory(TransactionFactory transactionFactory) {
this.transactionFactory = transactionFactory;
return this;
}
public Builder dataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
public String id() {
return this.id;
}
// 3、建造者
public Environment build() {
return new Environment(this.id, this.transactionFactory, this.dataSource);
}
}
public String getId() {
return this.id;
}
public TransactionFactory getTransactionFactory() {
return this.transactionFactory;
}
public DataSource getDataSource() {
return this.dataSource;
}
}
使用方式:
new Environment.Builder(id).transactionFactory(xx).dataSource(xx).build();
2、Swagger的接口管理的建造者模式,通过外部类来实现.
// 定义接口管理基本信息
public ApiInfo(
String title,
String description,
String version,
String termsOfServiceUrl,
Contact contact,
String license,
String licenseUrl,
Collection<VendorExtension> vendorExtensions) {
this.title = title;
this.description = description;
this.version = version;
this.termsOfServiceUrl = termsOfServiceUrl;
this.contact = contact;
this.license = license;
this.licenseUrl = licenseUrl;
this.vendorExtensions = new ArrayList<>(vendorExtensions);
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getTermsOfServiceUrl() {
return termsOfServiceUrl;
}
public Contact getContact() {
return contact;
}
public String getLicense() {
return license;
}
public String getLicenseUrl() {
return licenseUrl;
}
public String getVersion() {
return version;
}
public List<VendorExtension> getVendorExtensions() {
return vendorExtensions;
}
}
// 定义建造者类
public class ApiInfoBuilder {
private String title;
private String description;
private String termsOfServiceUrl;
private Contact contact;
private String license;
private String licenseUrl;
private String version;
private final List<VendorExtension> vendorExtensions = new ArrayList<>();
/**
* Updates the api title
*
* @param title - title for the API
* @return this
*/
public ApiInfoBuilder title(String title) {
this.title = title;
return this;
}
/**
* Updates the api description
*
* @param description - api description
* @return this
*/
public ApiInfoBuilder description(String description) {
this.description = description;
return this;
}
/**
* Updates the terms of service url
*
* @param termsOfServiceUrl - url to the terms of service
* @return this
*/
public ApiInfoBuilder termsOfServiceUrl(String termsOfServiceUrl) {
this.termsOfServiceUrl = termsOfServiceUrl;
return this;
}
/**
* Updates the api version
*
* @param version - of the API
* @return this
*/
public ApiInfoBuilder version(String version) {
this.version = version;
return this;
}
/**
* Updates contact information for the person responsible for this API
*
* @param contact - contact information
* @return this
*/
public ApiInfoBuilder contact(Contact contact) {
this.contact = contact;
return this;
}
/**
* Updates license information for this API
*
* @param license licence string
* @return this
*/
public ApiInfoBuilder license(String license) {
this.license = license;
return this;
}
/**
* Updates the license Url for this API
*
* @param licenseUrl - license Url
* @return this
*/
public ApiInfoBuilder licenseUrl(String licenseUrl) {
this.licenseUrl = licenseUrl;
return this;
}
/**
* Adds extensions for this API
*
* @param extensions - extensions
* @return this
*/
public ApiInfoBuilder extensions(List<VendorExtension> extensions) {
this.vendorExtensions.addAll(nullToEmptyList(extensions));
return this;
}
public ApiInfo build() {
return new ApiInfo(title, description, version, termsOfServiceUrl, contact, license, licenseUrl, vendorExtensions);
}
}
使用方式:
private ApiInfo apiInfo() { return new ApiInfoBuilder().title("Swagger3.x接口文档").description("项目前后端接口文档").contact(new Contact("Jack", "https://XXX", "xxx@qq.com")) .version("1.0.0").build(); }
3、Nacos的实例构造器建造者模式.
package com.alibaba.nacos.api.naming.pojo;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.naming.PreservedMetadataKeys;
import com.alibaba.nacos.api.utils.StringUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import static com.alibaba.nacos.api.common.Constants.NUMBER_PATTERN;
/**
* Instance.
*
* @author nkorange
*/
@JsonInclude(Include.NON_NULL)
public class Instance implements Serializable {
private static final long serialVersionUID = -742906310567291979L;
/**
* unique id of this instance.
*/
private String instanceId;
/**
* instance ip.
*/
private String ip;
/**
* instance port.
*/
private int port;
/**
* instance weight.
*/
private double weight = 1.0D;
/**
* instance health status.
*/
private boolean healthy = true;
/**
* If instance is enabled to accept request.
*/
private boolean enabled = true;
/**
* If instance is ephemeral.
*
* @since 1.0.0
*/
private boolean ephemeral = true;
/**
* cluster information of instance.
*/
private String clusterName;
/**
* Service information of instance.
*/
private String serviceName;
/**
* user extended attributes.
*/
private Map<String, String> metadata = new HashMap<String, String>();
public String getInstanceId() {
return this.instanceId;
}
public void setInstanceId(final String instanceId) {
this.instanceId = instanceId;
}
public String getIp() {
return this.ip;
}
public void setIp(final String ip) {
this.ip = ip;
}
public int getPort() {
return this.port;
}
public void setPort(final int port) {
this.port = port;
}
public double getWeight() {
return this.weight;
}
public void setWeight(final double weight) {
this.weight = weight;
}
public boolean isHealthy() {
return this.healthy;
}
public void setHealthy(final boolean healthy) {
this.healthy = healthy;
}
public String getClusterName() {
return this.clusterName;
}
public void setClusterName(final String clusterName) {
this.clusterName = clusterName;
}
public String getServiceName() {
return this.serviceName;
}
public void setServiceName(final String serviceName) {
this.serviceName = serviceName;
}
public Map<String, String> getMetadata() {
return this.metadata;
}
public void setMetadata(final Map<String, String> metadata) {
this.metadata = metadata;
}
/**
* add meta data.
*
* @param key meta data key
* @param value meta data value
*/
public void addMetadata(final String key, final String value) {
if (metadata == null) {
metadata = new HashMap<String, String>(4);
}
metadata.put(key, value);
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
public boolean isEphemeral() {
return this.ephemeral;
}
public void setEphemeral(final boolean ephemeral) {
this.ephemeral = ephemeral;
}
@Override
public String toString() {
return "Instance{" + "instanceId='" + instanceId + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", weight="
+ weight + ", healthy=" + healthy + ", enabled=" + enabled + ", ephemeral=" + ephemeral
+ ", clusterName='" + clusterName + '\'' + ", serviceName='" + serviceName + '\'' + ", metadata="
+ metadata + '}';
}
public String toInetAddr() {
return ip + ":" + port;
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof Instance)) {
return false;
}
final Instance host = (Instance) obj;
return Instance.strEquals(host.toString(), toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
private static boolean strEquals(final String str1, final String str2) {
return str1 == null ? str2 == null : str1.equals(str2);
}
public long getInstanceHeartBeatInterval() {
return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_INTERVAL,
Constants.DEFAULT_HEART_BEAT_INTERVAL);
}
public long getInstanceHeartBeatTimeOut() {
return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_TIMEOUT,
Constants.DEFAULT_HEART_BEAT_TIMEOUT);
}
public long getIpDeleteTimeout() {
return getMetaDataByKeyWithDefault(PreservedMetadataKeys.IP_DELETE_TIMEOUT,
Constants.DEFAULT_IP_DELETE_TIMEOUT);
}
public String getInstanceIdGenerator() {
return getMetaDataByKeyWithDefault(PreservedMetadataKeys.INSTANCE_ID_GENERATOR,
Constants.DEFAULT_INSTANCE_ID_GENERATOR);
}
/**
* Returns {@code true} if this metadata contains the specified key.
*
* @param key metadata key
* @return {@code true} if this metadata contains the specified key
*/
public boolean containsMetadata(final String key) {
if (getMetadata() == null || getMetadata().isEmpty()) {
return false;
}
return getMetadata().containsKey(key);
}
private long getMetaDataByKeyWithDefault(final String key, final long defaultValue) {
if (getMetadata() == null || getMetadata().isEmpty()) {
return defaultValue;
}
final String value = getMetadata().get(key);
if (!StringUtils.isEmpty(value) && value.matches(NUMBER_PATTERN)) {
return Long.parseLong(value);
}
return defaultValue;
}
private String getMetaDataByKeyWithDefault(final String key, final String defaultValue) {
if (getMetadata() == null || getMetadata().isEmpty()) {
return defaultValue;
}
return getMetadata().get(key);
}
}
package com.alibaba.nacos.api.naming.pojo.builder;
import com.alibaba.nacos.api.naming.pojo.Instance;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Builder for {@link Instance}.
*
* @author xiweng.yy
*/
public class InstanceBuilder {
private String instanceId;
private String ip;
private Integer port;
private Double weight;
private Boolean healthy;
private Boolean enabled;
private Boolean ephemeral;
private String clusterName;
private String serviceName;
private Map<String, String> metadata = new HashMap<>();
private InstanceBuilder() {
}
public InstanceBuilder setInstanceId(String instanceId) {
this.instanceId = instanceId;
return this;
}
public InstanceBuilder setIp(String ip) {
this.ip = ip;
return this;
}
public InstanceBuilder setPort(Integer port) {
this.port = port;
return this;
}
public InstanceBuilder setWeight(Double weight) {
this.weight = weight;
return this;
}
public InstanceBuilder setHealthy(Boolean healthy) {
this.healthy = healthy;
return this;
}
public InstanceBuilder setEnabled(Boolean enabled) {
this.enabled = enabled;
return this;
}
public InstanceBuilder setEphemeral(Boolean ephemeral) {
this.ephemeral = ephemeral;
return this;
}
public InstanceBuilder setClusterName(String clusterName) {
this.clusterName = clusterName;
return this;
}
public InstanceBuilder setServiceName(String serviceName) {
this.serviceName = serviceName;
return this;
}
public InstanceBuilder setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
return this;
}
public InstanceBuilder addMetadata(String metaKey, String metaValue) {
this.metadata.put(metaKey, metaValue);
return this;
}
/**
* Build a new {@link Instance}.
*
* @return new instance
*/
public Instance build() {
Instance result = new Instance();
if (!Objects.isNull(instanceId)) {
result.setInstanceId(instanceId);
}
if (!Objects.isNull(ip)) {
result.setIp(ip);
}
if (!Objects.isNull(port)) {
result.setPort(port);
}
if (!Objects.isNull(weight)) {
result.setWeight(weight);
}
if (!Objects.isNull(healthy)) {
result.setHealthy(healthy);
}
if (!Objects.isNull(enabled)) {
result.setEnabled(enabled);
}
if (!Objects.isNull(ephemeral)) {
result.setEphemeral(ephemeral);
}
if (!Objects.isNull(clusterName)) {
result.setClusterName(clusterName);
}
if (!Objects.isNull(serviceName)) {
result.setServiceName(serviceName);
}
result.setMetadata(metadata);
return result;
}
public static InstanceBuilder newBuilder() {
return new InstanceBuilder();
}
}
使用方式如下:
// 构造器方式. Instance instance = InstanceBuilder.newBuilder().setInstanceId(XXX).build();
4、Nacos的缓存构造器
package com.alibaba.nacos.common.cache.builder;
import com.alibaba.nacos.common.cache.Cache;
import com.alibaba.nacos.common.cache.decorators.AutoExpireCache;
import com.alibaba.nacos.common.cache.decorators.LruCache;
import com.alibaba.nacos.common.cache.decorators.SynchronizedCache;
import com.alibaba.nacos.common.cache.impl.SimpleCache;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* Cache builder.
* @author zzq
* @date 2021/7/30
*/
public class CacheBuilder<K, V> {
private static final int DEFAULT_MAXIMUMSIZE = 1024;
private static final int DEFAULT_INITIALIZE_CAPACITY = 1024;
private static final int DEFAULT_EXPIRE_NANOS = -1;
private long expireNanos = DEFAULT_EXPIRE_NANOS;
private int maximumSize = DEFAULT_MAXIMUMSIZE;
private int initializeCapacity = DEFAULT_INITIALIZE_CAPACITY;
private boolean sync = false;
private boolean lru = false;
public static <K, V> CacheBuilder<K, V> builder() {
return new CacheBuilder<>();
}
/**
* Set expiration time.
*/
public CacheBuilder<K, V> expireNanos(long duration, TimeUnit unit) {
checkExpireNanos(duration, unit);
this.expireNanos = unit.toNanos(duration);
return this;
}
private void checkExpireNanos(long duration, TimeUnit unit) {
if (duration < 0) {
throw new IllegalArgumentException("duration cannot be negative");
}
if (Objects.isNull(unit)) {
throw new IllegalArgumentException("unit cannot be null");
}
}
/**
* Set the maximum capacity of the cache pair.
* @param maximumSize maximum capacity
*/
public CacheBuilder<K, V> maximumSize(int maximumSize) {
if (maximumSize < 0) {
throw new IllegalArgumentException("size cannot be negative");
}
this.maximumSize = maximumSize;
return this;
}
/**
* Set whether the cache method is synchronized.
* @param sync if sync value is true, each method of the constructed cache is synchronized.
*/
public CacheBuilder<K, V> sync(boolean sync) {
this.sync = sync;
return this;
}
/**
* Does the constructed cache support lru.
* @param lru If the cache built for true is an lru cache.
*/
public CacheBuilder<K, V> lru(boolean lru) {
this.lru = lru;
return this;
}
/**
* Set the initial capacity of the cache pair.
* @param initializeCapacity initialize capacity
*/
public CacheBuilder<K, V> initializeCapacity(int initializeCapacity) {
if (initializeCapacity < 0) {
throw new IllegalArgumentException("initializeCapacity cannot be negative");
}
this.initializeCapacity = initializeCapacity;
return this;
}
/**
* Build the cache according to the builder attribute.
*/
public Cache<K, V> build() {
Cache<K, V> cache = new SimpleCache<>(initializeCapacity);
if (lru) {
cache = new LruCache<>(cache, maximumSize);
}
if (expireNanos != -1) {
cache = new AutoExpireCache<>(cache, expireNanos);
}
if (sync) {
cache = new SynchronizedCache<>(cache);
}
return cache;
}
}
private static final Cache<String, RateLimiter> CACHE; static { CACHE = CacheBuilder.<String, RateLimiter>builder() .expireNanos(1, TimeUnit.MINUTES) .initializeCapacity(CAPACITY_SIZE) .sync(true) .build(); }
5、Curator建造
Curator是Netflix公司开源的一套zookeeper客户端框架,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册Watcher和NodeExistsException异常等等。Patrixck Hunt(Zookeeper)以一句“Guava is to Java that Curator to Zookeeper”给Curator予高度评价。
public class CuratorFrameworkFactory
{
private static final int DEFAULT_SESSION_TIMEOUT_MS = Integer.getInteger("curator-default-session-timeout", 60 * 1000);
private static final int DEFAULT_CONNECTION_TIMEOUT_MS = Integer.getInteger("curator-default-connection-timeout", 15 * 1000);
private static final byte[] LOCAL_ADDRESS = getLocalAddress();
private static final CompressionProvider DEFAULT_COMPRESSION_PROVIDER = new GzipCompressionProvider();
private static final DefaultZookeeperFactory DEFAULT_ZOOKEEPER_FACTORY = new DefaultZookeeperFactory();
private static final DefaultACLProvider DEFAULT_ACL_PROVIDER = new DefaultACLProvider();
private static final long DEFAULT_INACTIVE_THRESHOLD_MS = (int)TimeUnit.MINUTES.toMillis(3);
private static final int DEFAULT_CLOSE_WAIT_MS = (int)TimeUnit.SECONDS.toMillis(1);
/**
* Return a new builder that builds a CuratorFramework
*
* @return new builder
*/
public static Builder builder()
{
return new Builder();
}
/**
* Create a new client with default session timeout and default connection timeout
*
* @param connectString list of servers to connect to
* @param retryPolicy retry policy to use
* @return client
*/
public static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy)
{
return newClient(connectString, DEFAULT_SESSION_TIMEOUT_MS, DEFAULT_CONNECTION_TIMEOUT_MS, retryPolicy);
}
/**
* Create a new client
*
* @param connectString list of servers to connect to
* @param sessionTimeoutMs session timeout
* @param connectionTimeoutMs connection timeout
* @param retryPolicy retry policy to use
* @return client
*/
public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy)
{
return builder().
connectString(connectString).
sessionTimeoutMs(sessionTimeoutMs).
connectionTimeoutMs(connectionTimeoutMs).
retryPolicy(retryPolicy).
build();
}
/**
* Return the local address as bytes that can be used as a node payload
*
* @return local address bytes
*/
public static byte[] getLocalAddress()
{
try
{
return InetAddress.getLocalHost().getHostAddress().getBytes();
}
catch ( UnknownHostException ignore )
{
// ignore
}
return new byte[0];
}
public static class Builder
{
private EnsembleProvider ensembleProvider;
private int sessionTimeoutMs = DEFAULT_SESSION_TIMEOUT_MS;
private int connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
private int maxCloseWaitMs = DEFAULT_CLOSE_WAIT_MS;
private RetryPolicy retryPolicy;
private ThreadFactory threadFactory = null;
private String namespace;
private List<AuthInfo> authInfos = null;
private byte[] defaultData = LOCAL_ADDRESS;
private CompressionProvider compressionProvider = DEFAULT_COMPRESSION_PROVIDER;
private ZookeeperFactory zookeeperFactory = DEFAULT_ZOOKEEPER_FACTORY;
private ACLProvider aclProvider = DEFAULT_ACL_PROVIDER;
private boolean canBeReadOnly = false;
private boolean useContainerParentsIfAvailable = true;
private ConnectionStateErrorPolicy connectionStateErrorPolicy = new StandardConnectionStateErrorPolicy();
private ConnectionHandlingPolicy connectionHandlingPolicy = new StandardConnectionHandlingPolicy();
private SchemaSet schemaSet = SchemaSet.getDefaultSchemaSet();
private boolean zk34CompatibilityMode = isZK34();
/**
* Apply the current values and build a new CuratorFramework
*
* @return new CuratorFramework
*/
public CuratorFramework build()
{
return new CuratorFrameworkImpl(this);
}
/**
* Apply the current values and build a new temporary CuratorFramework. Temporary
* CuratorFramework instances are meant for single requests to ZooKeeper ensembles
* over a failure prone network such as a WAN. The APIs available from {@link CuratorTempFramework}
* are limited. Further, the connection will be closed after 3 minutes of inactivity.
*
* @return temp instance
*/
public CuratorTempFramework buildTemp()
{
return buildTemp(DEFAULT_INACTIVE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
}
/**
* Apply the current values and build a new temporary CuratorFramework. Temporary
* CuratorFramework instances are meant for single requests to ZooKeeper ensembles
* over a failure prone network such as a WAN. The APIs available from {@link CuratorTempFramework}
* are limited. Further, the connection will be closed after <code>inactiveThresholdMs</code> milliseconds of inactivity.
*
* @param inactiveThreshold number of milliseconds of inactivity to cause connection close
* @param unit threshold unit
* @return temp instance
*/
public CuratorTempFramework buildTemp(long inactiveThreshold, TimeUnit unit)
{
return new CuratorTempFrameworkImpl(this, unit.toMillis(inactiveThreshold));
}
/**
* Add connection authorization
*
* Subsequent calls to this method overwrite the prior calls.
*
* @param scheme the scheme
* @param auth the auth bytes
* @return this
*/
public Builder authorization(String scheme, byte[] auth)
{
return authorization(ImmutableList.of(new AuthInfo(scheme, (auth != null) ? Arrays.copyOf(auth, auth.length) : null)));
}
/**
* Add connection authorization. The supplied authInfos are appended to those added via call to
* {@link #authorization(java.lang.String, byte[])} for backward compatibility.
* <p/>
* Subsequent calls to this method overwrite the prior calls.
*
* @param authInfos list of {@link AuthInfo} objects with scheme and auth
* @return this
*/
public Builder authorization(List<AuthInfo> authInfos)
{
this.authInfos = ImmutableList.copyOf(authInfos);
return this;
}
/**
* Set the list of servers to connect to. IMPORTANT: use either this or {@link #ensembleProvider(EnsembleProvider)}
* but not both.
*
* @param connectString list of servers to connect to
* @return this
*/
public Builder connectString(String connectString)
{
ensembleProvider = new FixedEnsembleProvider(connectString);
return this;
}
/**
* Set the list ensemble provider. IMPORTANT: use either this or {@link #connectString(String)}
* but not both.
*
* @param ensembleProvider the ensemble provider to use
* @return this
*/
public Builder ensembleProvider(EnsembleProvider ensembleProvider)
{
this.ensembleProvider = ensembleProvider;
return this;
}
/**
* Sets the data to use when {@link PathAndBytesable#forPath(String)} is used.
* This is useful for debugging purposes. For example, you could set this to be the IP of the
* client.
*
* @param defaultData new default data to use
* @return this
*/
public Builder defaultData(byte[] defaultData)
{
this.defaultData = (defaultData != null) ? Arrays.copyOf(defaultData, defaultData.length) : null;
return this;
}
/**
* As ZooKeeper is a shared space, users of a given cluster should stay within
* a pre-defined namespace. If a namespace is set here, all paths will get pre-pended
* with the namespace
*
* @param namespace the namespace
* @return this
*/
public Builder namespace(String namespace)
{
this.namespace = namespace;
return this;
}
/**
* @param sessionTimeoutMs session timeout
* @return this
*/
public Builder sessionTimeoutMs(int sessionTimeoutMs)
{
this.sessionTimeoutMs = sessionTimeoutMs;
return this;
}
/**
* @param connectionTimeoutMs connection timeout
* @return this
*/
public Builder connectionTimeoutMs(int connectionTimeoutMs)
{
this.connectionTimeoutMs = connectionTimeoutMs;
return this;
}
/**
* @param maxCloseWaitMs time to wait during close to join background threads
* @return this
*/
public Builder maxCloseWaitMs(int maxCloseWaitMs)
{
this.maxCloseWaitMs = maxCloseWaitMs;
return this;
}
/**
* @param retryPolicy retry policy to use
* @return this
*/
public Builder retryPolicy(RetryPolicy retryPolicy)
{
this.retryPolicy = retryPolicy;
return this;
}
/**
* @param threadFactory thread factory used to create Executor Services
* @return this
*/
public Builder threadFactory(ThreadFactory threadFactory)
{
this.threadFactory = threadFactory;
return this;
}
/**
* @param compressionProvider the compression provider
* @return this
*/
public Builder compressionProvider(CompressionProvider compressionProvider)
{
this.compressionProvider = compressionProvider;
return this;
}
/**
* @param zookeeperFactory the zookeeper factory to use
* @return this
*/
public Builder zookeeperFactory(ZookeeperFactory zookeeperFactory)
{
this.zookeeperFactory = zookeeperFactory;
return this;
}
/**
* @param aclProvider a provider for ACLs
* @return this
*/
public Builder aclProvider(ACLProvider aclProvider)
{
this.aclProvider = aclProvider;
return this;
}
/**
* @param canBeReadOnly if true, allow ZooKeeper client to enter
* read only mode in case of a network partition. See
* {@link ZooKeeper#ZooKeeper(String, int, Watcher, long, byte[], boolean)}
* for details
* @return this
*/
public Builder canBeReadOnly(boolean canBeReadOnly)
{
this.canBeReadOnly = canBeReadOnly;
return this;
}
/**
* By default, Curator uses {@link CreateBuilder#creatingParentContainersIfNeeded()}
* if the ZK JAR supports {@link CreateMode#CONTAINER}. Call this method to turn off this behavior.
*
* @return this
*/
public Builder dontUseContainerParents()
{
this.useContainerParentsIfAvailable = false;
return this;
}
/**
* Set the error policy to use. The default is {@link StandardConnectionStateErrorPolicy}
*
* @since 3.0.0
* @param connectionStateErrorPolicy new error policy
* @return this
*/
public Builder connectionStateErrorPolicy(ConnectionStateErrorPolicy connectionStateErrorPolicy)
{
this.connectionStateErrorPolicy = connectionStateErrorPolicy;
return this;
}
/**
* If mode is true, create a ZooKeeper 3.4.x compatible client. IMPORTANT: If the client
* library used is ZooKeeper 3.4.x <code>zk34CompatibilityMode</code> is enabled by default.
*
* @since 3.5.0
* @param mode true/false
* @return this
*/
public Builder zk34CompatibilityMode(boolean mode)
{
this.zk34CompatibilityMode = mode;
return this;
}
/**
* <p>
* Change the connection handling policy. The default policy is {@link StandardConnectionHandlingPolicy}.
* </p>
* <p>
* <strong>IMPORTANT: </strong> StandardConnectionHandlingPolicy has different behavior than the connection
* policy handling prior to version 3.0.0.
* </p>
* <p>
* Major differences from the older behavior are:
* </p>
* <ul>
* <li>
* Session/connection timeouts are no longer managed by the low-level client. They are managed
* by the CuratorFramework instance. There should be no noticeable differences.
* </li>
* <li>
* Prior to 3.0.0, each iteration of the retry policy would allow the connection timeout to elapse
* if the connection hadn't yet succeeded. This meant that the true connection timeout was the configured
* value times the maximum retries in the retry policy. This longstanding issue has been address.
* Now, the connection timeout can elapse only once for a single API call.
* </li>
* <li>
* <strong>MOST IMPORTANTLY!</strong> Prior to 3.0.0, {@link ConnectionState#LOST} did not imply
* a lost session (much to the confusion of users). Now,
* Curator will set the LOST state only when it believes that the ZooKeeper session
* has expired. ZooKeeper connections have a session. When the session expires, clients must take appropriate
* action. In Curator, this is complicated by the fact that Curator internally manages the ZooKeeper
* connection. Now, Curator will set the LOST state when any of the following occurs:
* a) ZooKeeper returns a {@link Watcher.Event.KeeperState#Expired} or {@link KeeperException.Code#SESSIONEXPIRED};
* b) Curator closes the internally managed ZooKeeper instance; c) The session timeout
* elapses during a network partition.
* </li>
* </ul>
*
* @param connectionHandlingPolicy the policy
* @return this
* @since 3.0.0
*/
public Builder connectionHandlingPolicy(ConnectionHandlingPolicy connectionHandlingPolicy)
{
this.connectionHandlingPolicy = connectionHandlingPolicy;
return this;
}
/**
* Add an enforced schema set
*
* @param schemaSet the schema set
* @return this
* @since 3.2.0
*/
public Builder schemaSet(SchemaSet schemaSet)
{
this.schemaSet = schemaSet;
return this;
}
public ACLProvider getAclProvider()
{
return aclProvider;
}
public ZookeeperFactory getZookeeperFactory()
{
return zookeeperFactory;
}
public CompressionProvider getCompressionProvider()
{
return compressionProvider;
}
public ThreadFactory getThreadFactory()
{
return threadFactory;
}
public EnsembleProvider getEnsembleProvider()
{
return ensembleProvider;
}
public int getSessionTimeoutMs()
{
return sessionTimeoutMs;
}
public int getConnectionTimeoutMs()
{
return connectionTimeoutMs;
}
public int getMaxCloseWaitMs()
{
return maxCloseWaitMs;
}
public RetryPolicy getRetryPolicy()
{
return retryPolicy;
}
public String getNamespace()
{
return namespace;
}
public boolean useContainerParentsIfAvailable()
{
return useContainerParentsIfAvailable;
}
public ConnectionStateErrorPolicy getConnectionStateErrorPolicy()
{
return connectionStateErrorPolicy;
}
public ConnectionHandlingPolicy getConnectionHandlingPolicy()
{
return connectionHandlingPolicy;
}
public SchemaSet getSchemaSet()
{
return schemaSet;
}
public boolean isZk34CompatibilityMode()
{
return zk34CompatibilityMode;
}
@Deprecated
public String getAuthScheme()
{
int qty = (authInfos != null) ? authInfos.size() : 0;
switch ( qty )
{
case 0:
{
return null;
}
case 1:
{
return authInfos.get(0).scheme;
}
default:
{
throw new IllegalStateException("More than 1 auth has been added");
}
}
}
@Deprecated
public byte[] getAuthValue()
{
int qty = (authInfos != null) ? authInfos.size() : 0;
switch ( qty )
{
case 0:
{
return null;
}
case 1:
{
byte[] bytes = authInfos.get(0).getAuth();
return (bytes != null) ? Arrays.copyOf(bytes, bytes.length) : null;
}
default:
{
throw new IllegalStateException("More than 1 auth has been added");
}
}
}
public List<AuthInfo> getAuthInfos()
{
return authInfos;
}
public byte[] getDefaultData()
{
return defaultData;
}
public boolean canBeReadOnly()
{
return canBeReadOnly;
}
private Builder()
{
}
}
private CuratorFrameworkFactory()
{
}
}
/** * Create a new client with default session timeout and default connection timeout * * @param connectString list of servers to connect to * @param retryPolicy retry policy to use * @return client */ public static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy) { return newClient(connectString, DEFAULT_SESSION_TIMEOUT_MS, DEFAULT_CONNECTION_TIMEOUT_MS, retryPolicy); } /** * Create a new client * * @param connectString list of servers to connect to * @param sessionTimeoutMs session timeout * @param connectionTimeoutMs connection timeout * @param retryPolicy retry policy to use * @return client */ public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy) { return builder(). connectString(connectString). sessionTimeoutMs(sessionTimeoutMs). connectionTimeoutMs(connectionTimeoutMs). retryPolicy(retryPolicy). build(); }
使用静态工程方法创建客户端
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 3000, 3000, exponentialBackoffRetry);
使用Fluent风格的Api创建客户端
// 创建一个CuratorFramework实例
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("localhost:2181") // ZooKeeper连接地址
.sessionTimeoutMs(5000) // 会话超时时间
.retryPolicy(new ExponentialBackoffRetry(1000, 3)) // 重试策略
.build();
6、SpringBean的创造
BeanDefinition
package org.springframework.beans.factory.config;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.AttributeAccessor;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
/**
* A BeanDefinition describes a bean instance, which has property values,
* constructor argument values, and further information supplied by
* concrete implementations.
*
* <p>This is just a minimal interface: The main intention is to allow a
* {@link BeanFactoryPostProcessor} to introspect and modify property values
* and other bean metadata.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 19.03.2004
* @see ConfigurableListableBeanFactory#getBeanDefinition
* @see org.springframework.beans.factory.support.RootBeanDefinition
* @see org.springframework.beans.factory.support.ChildBeanDefinition
*/
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* Scope identifier for the standard singleton scope: {@value}.
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
*/
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* Scope identifier for the standard prototype scope: {@value}.
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
*/
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
/**
* Role hint indicating that a {@code BeanDefinition} is a major part
* of the application. Typically corresponds to a user-defined bean.
*/
int ROLE_APPLICATION = 0;
/**
* Role hint indicating that a {@code BeanDefinition} is a supporting
* part of some larger configuration, typically an outer
* {@link org.springframework.beans.factory.parsing.ComponentDefinition}.
* {@code SUPPORT} beans are considered important enough to be aware
* of when looking more closely at a particular
* {@link org.springframework.beans.factory.parsing.ComponentDefinition},
* but not when looking at the overall configuration of an application.
*/
int ROLE_SUPPORT = 1;
/**
* Role hint indicating that a {@code BeanDefinition} is providing an
* entirely background role and has no relevance to the end-user. This hint is
* used when registering beans that are completely part of the internal workings
* of a {@link org.springframework.beans.factory.parsing.ComponentDefinition}.
*/
int ROLE_INFRASTRUCTURE = 2;
// Modifiable attributes
/**
* Set the name of the parent definition of this bean definition, if any.
*/
void setParentName(@Nullable String parentName);
/**
* Return the name of the parent definition of this bean definition, if any.
*/
@Nullable
String getParentName();
/**
* Specify the bean class name of this bean definition.
* <p>The class name can be modified during bean factory post-processing,
* typically replacing the original class name with a parsed variant of it.
* @see #setParentName
* @see #setFactoryBeanName
* @see #setFactoryMethodName
*/
void setBeanClassName(@Nullable String beanClassName);
/**
* Return the current bean class name of this bean definition.
* <p>Note that this does not have to be the actual class name used at runtime, in
* case of a child definition overriding/inheriting the class name from its parent.
* Also, this may just be the class that a factory method is called on, or it may
* even be empty in case of a factory bean reference that a method is called on.
* Hence, do <i>not</i> consider this to be the definitive bean type at runtime but
* rather only use it for parsing purposes at the individual bean definition level.
* @see #getParentName()
* @see #getFactoryBeanName()
* @see #getFactoryMethodName()
*/
@Nullable
String getBeanClassName();
/**
* Override the target scope of this bean, specifying a new scope name.
* @see #SCOPE_SINGLETON
* @see #SCOPE_PROTOTYPE
*/
void setScope(@Nullable String scope);
/**
* Return the name of the current target scope for this bean,
* or {@code null} if not known yet.
*/
@Nullable
String getScope();
/**
* Set whether this bean should be lazily initialized.
* <p>If {@code false}, the bean will get instantiated on startup by bean
* factories that perform eager initialization of singletons.
*/
void setLazyInit(boolean lazyInit);
/**
* Return whether this bean should be lazily initialized, i.e. not
* eagerly instantiated on startup. Only applicable to a singleton bean.
*/
boolean isLazyInit();
/**
* Set the names of the beans that this bean depends on being initialized.
* The bean factory will guarantee that these beans get initialized first.
*/
void setDependsOn(@Nullable String... dependsOn);
/**
* Return the bean names that this bean depends on.
*/
@Nullable
String[] getDependsOn();
/**
* Set whether this bean is a candidate for getting autowired into some other bean.
* <p>Note that this flag is designed to only affect type-based autowiring.
* It does not affect explicit references by name, which will get resolved even
* if the specified bean is not marked as an autowire candidate. As a consequence,
* autowiring by name will nevertheless inject a bean if the name matches.
*/
void setAutowireCandidate(boolean autowireCandidate);
/**
* Return whether this bean is a candidate for getting autowired into some other bean.
*/
boolean isAutowireCandidate();
/**
* Set whether this bean is a primary autowire candidate.
* <p>If this value is {@code true} for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
void setPrimary(boolean primary);
/**
* Return whether this bean is a primary autowire candidate.
*/
boolean isPrimary();
/**
* Specify the factory bean to use, if any.
* This the name of the bean to call the specified factory method on.
* @see #setFactoryMethodName
*/
void setFactoryBeanName(@Nullable String factoryBeanName);
/**
* Return the factory bean name, if any.
*/
@Nullable
String getFactoryBeanName();
/**
* Specify a factory method, if any. This method will be invoked with
* constructor arguments, or with no arguments if none are specified.
* The method will be invoked on the specified factory bean, if any,
* or otherwise as a static method on the local bean class.
* @see #setFactoryBeanName
* @see #setBeanClassName
*/
void setFactoryMethodName(@Nullable String factoryMethodName);
/**
* Return a factory method, if any.
*/
@Nullable
String getFactoryMethodName();
/**
* Return the constructor argument values for this bean.
* <p>The returned instance can be modified during bean factory post-processing.
* @return the ConstructorArgumentValues object (never {@code null})
*/
ConstructorArgumentValues getConstructorArgumentValues();
/**
* Return if there are constructor argument values defined for this bean.
* @since 5.0.2
*/
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
/**
* Return the property values to be applied to a new instance of the bean.
* <p>The returned instance can be modified during bean factory post-processing.
* @return the MutablePropertyValues object (never {@code null})
*/
MutablePropertyValues getPropertyValues();
/**
* Return if there are property values defined for this bean.
* @since 5.0.2
*/
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
/**
* Set the name of the initializer method.
* @since 5.1
*/
void setInitMethodName(@Nullable String initMethodName);
/**
* Return the name of the initializer method.
* @since 5.1
*/
@Nullable
String getInitMethodName();
/**
* Set the name of the destroy method.
* @since 5.1
*/
void setDestroyMethodName(@Nullable String destroyMethodName);
/**
* Return the name of the destroy method.
* @since 5.1
*/
@Nullable
String getDestroyMethodName();
/**
* Set the role hint for this {@code BeanDefinition}. The role hint
* provides the frameworks as well as tools an indication of
* the role and importance of a particular {@code BeanDefinition}.
* @since 5.1
* @see #ROLE_APPLICATION
* @see #ROLE_SUPPORT
* @see #ROLE_INFRASTRUCTURE
*/
void setRole(int role);
/**
* Get the role hint for this {@code BeanDefinition}. The role hint
* provides the frameworks as well as tools an indication of
* the role and importance of a particular {@code BeanDefinition}.
* @see #ROLE_APPLICATION
* @see #ROLE_SUPPORT
* @see #ROLE_INFRASTRUCTURE
*/
int getRole();
/**
* Set a human-readable description of this bean definition.
* @since 5.1
*/
void setDescription(@Nullable String description);
/**
* Return a human-readable description of this bean definition.
*/
@Nullable
String getDescription();
// Read-only attributes
/**
* Return a resolvable type for this bean definition,
* based on the bean class or other specific metadata.
* <p>This is typically fully resolved on a runtime-merged bean definition
* but not necessarily on a configuration-time definition instance.
* @return the resolvable type (potentially {@link ResolvableType#NONE})
* @since 5.2
* @see ConfigurableBeanFactory#getMergedBeanDefinition
*/
ResolvableType getResolvableType();
/**
* Return whether this a <b>Singleton</b>, with a single, shared instance
* returned on all calls.
* @see #SCOPE_SINGLETON
*/
boolean isSingleton();
/**
* Return whether this a <b>Prototype</b>, with an independent instance
* returned for each call.
* @since 3.0
* @see #SCOPE_PROTOTYPE
*/
boolean isPrototype();
/**
* Return whether this bean is "abstract", that is, not meant to be instantiated.
*/
boolean isAbstract();
/**
* Return a description of the resource that this bean definition
* came from (for the purpose of showing context in case of errors).
*/
@Nullable
String getResourceDescription();
/**
* Return the originating BeanDefinition, or {@code null} if none.
* <p>Allows for retrieving the decorated bean definition, if any.
* <p>Note that this method returns the immediate originator. Iterate through the
* originator chain to find the original BeanDefinition as defined by the user.
*/
@Nullable
BeanDefinition getOriginatingBeanDefinition();
}
BeanDefinitionBuilder
二、模板模板.
1、MyBatis的类型处理器的TypeHandler的实现.实现JavaType<->JdbcType的相互转换.
// 定义接口
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* @param columnName Colunm name, when configuration <code>useColumnLabel</code> is <code>false</code>
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
// 抽象类实现模板的公共逻辑和由子类要去实现的各自子逻辑
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
/**
* @deprecated Since 3.5.0 - See https://github.com/mybatis/mybatis-3/issues/1203. This field will remove future.
*/
@Deprecated
protected Configuration configuration;
/**
* @deprecated Since 3.5.0 - See https://github.com/mybatis/mybatis-3/issues/1203. This property will remove future.
*/
@Deprecated
public void setConfiguration(Configuration c) {
this.configuration = c;
}
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
+ "Cause: " + e, e);
}
} else {
try {
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different configuration property. "
+ "Cause: " + e, e);
}
}
}
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
try {
return getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
}
@Override
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
try {
return getNullableResult(rs, columnIndex);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column #" + columnIndex + " from result set. Cause: " + e, e);
}
}
@Override
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
try {
return getNullableResult(cs, columnIndex);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column #" + columnIndex + " from callable statement. Cause: " + e, e);
}
}
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* @param columnName Colunm name, when configuration <code>useColumnLabel</code> is <code>false</code>
*/
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
}
// 具体类实现父类的抽象逻辑即可.
public class IntegerTypeHandler extends BaseTypeHandler<Integer> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
throws SQLException {
ps.setInt(i, parameter);
}
@Override
public Integer getNullableResult(ResultSet rs, String columnName)
throws SQLException {
int result = rs.getInt(columnName);
return result == 0 && rs.wasNull() ? null : result;
}
@Override
public Integer getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
int result = rs.getInt(columnIndex);
return result == 0 && rs.wasNull() ? null : result;
}
@Override
public Integer getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
int result = cs.getInt(columnIndex);
return result == 0 && cs.wasNull() ? null : result;
}
}
三、MyBatis的单例模式.
1、ErrorContext的单例模式.
public class ErrorContext {
private static final String LINE_SEPARATOR = System.getProperty("line.separator","\n");
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<>();
private ErrorContext stored;
private String resource;
private String activity;
private String object;
private String message;
private String sql;
private Throwable cause;
private ErrorContext() {
}
public static ErrorContext instance() {
ErrorContext context = LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
public ErrorContext store() {
ErrorContext newContext = new ErrorContext();
newContext.stored = this;
LOCAL.set(newContext);
return LOCAL.get();
}
public ErrorContext recall() {
if (stored != null) {
LOCAL.set(stored);
stored = null;
}
return LOCAL.get();
}
public ErrorContext resource(String resource) {
this.resource = resource;
return this;
}
public ErrorContext activity(String activity) {
this.activity = activity;
return this;
}
public ErrorContext object(String object) {
this.object = object;
return this;
}
public ErrorContext message(String message) {
this.message = message;
return this;
}
public ErrorContext sql(String sql) {
this.sql = sql;
return this;
}
public ErrorContext cause(Throwable cause) {
this.cause = cause;
return this;
}
public ErrorContext reset() {
resource = null;
activity = null;
object = null;
message = null;
sql = null;
cause = null;
LOCAL.remove();
return this;
}
@Override
public String toString() {
StringBuilder description = new StringBuilder();
// message
if (this.message != null) {
description.append(LINE_SEPARATOR);
description.append("### ");
description.append(this.message);
}
// resource
if (resource != null) {
description.append(LINE_SEPARATOR);
description.append("### The error may exist in ");
description.append(resource);
}
// object
if (object != null) {
description.append(LINE_SEPARATOR);
description.append("### The error may involve ");
description.append(object);
}
// activity
if (activity != null) {
description.append(LINE_SEPARATOR);
description.append("### The error occurred while ");
description.append(activity);
}
// activity
if (sql != null) {
description.append(LINE_SEPARATOR);
description.append("### SQL: ");
description.append(sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim());
}
// cause
if (cause != null) {
description.append(LINE_SEPARATOR);
description.append("### Cause: ");
description.append(cause.toString());
}
return description.toString();
}
}
四、标记接口.
1、Spring框架的标记接口,标记一类用于感知自己属性的接口,无抽象只有空接口标记,具体业务有子接口自己定义.
public interface Aware {
}
所有的子接口如下
比如我们熟悉的三个接口:
ApplicationContextAware
// 用于感知ApplicationContext的接口
public interface ApplicationContextAware extends Aware {
/**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
BeanFactoryAware
public interface BeanFactoryAware extends Aware {
/**
* Callback that supplies the owning factory to a bean instance.
* <p>Invoked after the population of normal bean properties
* but before an initialization callback such as
* {@link InitializingBean#afterPropertiesSet()} or a custom init-method.
* @param beanFactory owning BeanFactory (never {@code null}).
* The bean can immediately call methods on the factory.
* @throws BeansException in case of initialization errors
* @see BeanInitializationException
*/
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
BeanNameAware
// 感知BeanName
public interface BeanNameAware extends Aware {
/**
* Set the name of the bean in the bean factory that created this bean.
* <p>Invoked after population of normal bean properties but before an
* init callback such as {@link InitializingBean#afterPropertiesSet()}
* or a custom init-method.
* @param name the name of the bean in the factory.
* Note that this name is the actual bean name used in the factory, which may
* differ from the originally specified name: in particular for inner bean
* names, the actual bean name might have been made unique through appending
* "#..." suffixes. Use the {@link BeanFactoryUtils#originalBeanName(String)}
* method to extract the original bean name (without suffix), if desired.
*/
void setBeanName(String name);
}
在java中Serializable,Cloneable,RandomAccess这些接口的内部没有任何方法,但是实现了这些接口以后便可以序列化,拷贝以及判断集合是否能快速访问。原因是应为这些接口都是标记接口,它们的功能就是标记实现给接口的类拥有对应的功能。
五、工厂模式.
1、MyBatis的数据源,工厂模式的说明.
工厂接口:外部使用会直接与工厂接口交互用于获取具体的产品实现类.
具体工厂类:不同的具体工厂类会根据需求实例化不同的产品实现类.
产品接口:用于定义产品类的功能.
具体产品类:具体的产品类实现了具体的产品逻辑.
【数据源工厂】DataSourceFactory:
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
【具体类型数据源类】
【产品接口】
【具体产品类】
具体工厂子类如果生产具体产品
PooledDataSourceFactory
池状态计算,线程安全的计算.
Spring的内置数据库连接
工厂类:DataSourceFactory
package org.springframework.jdbc.datasource.embedded;
import javax.sql.DataSource;
/**
* {@code DataSourceFactory} encapsulates the creation of a particular
* {@link DataSource} implementation such as a non-pooling
* {@link org.springframework.jdbc.datasource.SimpleDriverDataSource}
* or a HikariCP pool setup in the shape of a {@code HikariDataSource}.
*
* <p>Call {@link #getConnectionProperties()} to configure normalized
* {@code DataSource} properties before calling {@link #getDataSource()}
* to actually get the configured {@code DataSource} instance.
*
* @author Keith Donald
* @author Sam Brannen
* @since 3.0
*/
public interface DataSourceFactory {
/**
* Get the {@linkplain ConnectionProperties connection properties}
* of the {@link #getDataSource DataSource} to be configured.
*/
ConnectionProperties getConnectionProperties();
/**
* Get the {@link DataSource} with the
* {@linkplain #getConnectionProperties connection properties} applied.
*/
DataSource getDataSource();
}
EmbeddedDatabaseFactory
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.datasource.embedded;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.UUID;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Factory for creating an {@link EmbeddedDatabase} instance.
*
* <p>Callers are guaranteed that the returned database has been fully
* initialized and populated.
*
* <p>The factory can be configured as follows:
* <ul>
* <li>Call {@link #generateUniqueDatabaseName} to set a unique, random name
* for the database.
* <li>Call {@link #setDatabaseName} to set an explicit name for the database.
* <li>Call {@link #setDatabaseType} to set the database type if you wish to
* use one of the supported types.
* <li>Call {@link #setDatabaseConfigurer} to configure support for a custom
* embedded database type.
* <li>Call {@link #setDatabasePopulator} to change the algorithm used to
* populate the database.
* <li>Call {@link #setDataSourceFactory} to change the type of
* {@link DataSource} used to connect to the database.
* </ul>
*
* <p>After configuring the factory, call {@link #getDatabase()} to obtain
* a reference to the {@link EmbeddedDatabase} instance.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class EmbeddedDatabaseFactory {
/**
* Default name for an embedded database: {@value}.
*/
public static final String DEFAULT_DATABASE_NAME = "testdb";
private static final Log logger = LogFactory.getLog(EmbeddedDatabaseFactory.class);
private boolean generateUniqueDatabaseName = false;
private String databaseName = DEFAULT_DATABASE_NAME;
private DataSourceFactory dataSourceFactory = new SimpleDriverDataSourceFactory();
@Nullable
private EmbeddedDatabaseConfigurer databaseConfigurer;
@Nullable
private DatabasePopulator databasePopulator;
@Nullable
private DataSource dataSource;
/**
* Set the {@code generateUniqueDatabaseName} flag to enable or disable
* generation of a pseudo-random unique ID to be used as the database name.
* <p>Setting this flag to {@code true} overrides any explicit name set
* via {@link #setDatabaseName}.
* @since 4.2
* @see #setDatabaseName
*/
public void setGenerateUniqueDatabaseName(boolean generateUniqueDatabaseName) {
this.generateUniqueDatabaseName = generateUniqueDatabaseName;
}
/**
* Set the name of the database.
* <p>Defaults to {@value #DEFAULT_DATABASE_NAME}.
* <p>Will be overridden if the {@code generateUniqueDatabaseName} flag
* has been set to {@code true}.
* @param databaseName name of the embedded database
* @see #setGenerateUniqueDatabaseName
*/
public void setDatabaseName(String databaseName) {
Assert.hasText(databaseName, "Database name is required");
this.databaseName = databaseName;
}
/**
* Set the factory to use to create the {@link DataSource} instance that
* connects to the embedded database.
* <p>Defaults to {@link SimpleDriverDataSourceFactory}.
*/
public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
Assert.notNull(dataSourceFactory, "DataSourceFactory is required");
this.dataSourceFactory = dataSourceFactory;
}
/**
* Set the type of embedded database to use.
* <p>Call this when you wish to configure one of the pre-supported types.
* <p>Defaults to HSQL.
* @param type the database type
*/
public void setDatabaseType(EmbeddedDatabaseType type) {
this.databaseConfigurer = EmbeddedDatabaseConfigurerFactory.getConfigurer(type);
}
/**
* Set the strategy that will be used to configure the embedded database instance.
* <p>Call this when you wish to use an embedded database type not already supported.
*/
public void setDatabaseConfigurer(EmbeddedDatabaseConfigurer configurer) {
this.databaseConfigurer = configurer;
}
/**
* Set the strategy that will be used to initialize or populate the embedded
* database.
* <p>Defaults to {@code null}.
*/
public void setDatabasePopulator(DatabasePopulator populator) {
this.databasePopulator = populator;
}
/**
* Factory method that returns the {@linkplain EmbeddedDatabase embedded database}
* instance, which is also a {@link DataSource}.
*/
public EmbeddedDatabase getDatabase() {
if (this.dataSource == null) {
initDatabase();
}
return new EmbeddedDataSourceProxy(this.dataSource);
}
/**
* Hook to initialize the embedded database.
* <p>If the {@code generateUniqueDatabaseName} flag has been set to {@code true},
* the current value of the {@linkplain #setDatabaseName database name} will
* be overridden with an auto-generated name.
* <p>Subclasses may call this method to force initialization; however,
* this method should only be invoked once.
* <p>After calling this method, {@link #getDataSource()} returns the
* {@link DataSource} providing connectivity to the database.
*/
protected void initDatabase() {
if (this.generateUniqueDatabaseName) {
setDatabaseName(UUID.randomUUID().toString());
}
// Create the embedded database first
if (this.databaseConfigurer == null) {
this.databaseConfigurer = EmbeddedDatabaseConfigurerFactory.getConfigurer(EmbeddedDatabaseType.HSQL);
}
this.databaseConfigurer.configureConnectionProperties(
this.dataSourceFactory.getConnectionProperties(), this.databaseName);
this.dataSource = this.dataSourceFactory.getDataSource();
if (logger.isInfoEnabled()) {
if (this.dataSource instanceof SimpleDriverDataSource) {
SimpleDriverDataSource simpleDriverDataSource = (SimpleDriverDataSource) this.dataSource;
logger.info(String.format("Starting embedded database: url='%s', username='%s'",
simpleDriverDataSource.getUrl(), simpleDriverDataSource.getUsername()));
}
else {
logger.info(String.format("Starting embedded database '%s'", this.databaseName));
}
}
// Now populate the database
if (this.databasePopulator != null) {
try {
DatabasePopulatorUtils.execute(this.databasePopulator, this.dataSource);
}
catch (RuntimeException ex) {
// failed to populate, so leave it as not initialized
shutdownDatabase();
throw ex;
}
}
}
/**
* Hook to shutdown the embedded database. Subclasses may call this method
* to force shutdown.
* <p>After calling, {@link #getDataSource()} returns {@code null}.
* <p>Does nothing if no embedded database has been initialized.
*/
protected void shutdownDatabase() {
if (this.dataSource != null) {
if (logger.isInfoEnabled()) {
if (this.dataSource instanceof SimpleDriverDataSource) {
logger.info(String.format("Shutting down embedded database: url='%s'",
((SimpleDriverDataSource) this.dataSource).getUrl()));
}
else {
logger.info(String.format("Shutting down embedded database '%s'", this.databaseName));
}
}
if (this.databaseConfigurer != null) {
this.databaseConfigurer.shutdown(this.dataSource, this.databaseName);
}
this.dataSource = null;
}
}
/**
* Hook that gets the {@link DataSource} that provides the connectivity to the
* embedded database.
* <p>Returns {@code null} if the {@code DataSource} has not been initialized
* or if the database has been shut down. Subclasses may call this method to
* access the {@code DataSource} instance directly.
*/
@Nullable
protected final DataSource getDataSource() {
return this.dataSource;
}
private class EmbeddedDataSourceProxy implements EmbeddedDatabase {
private final DataSource dataSource;
public EmbeddedDataSourceProxy(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Connection getConnection() throws SQLException {
return this.dataSource.getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return this.dataSource.getConnection(username, password);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return this.dataSource.getLogWriter();
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
this.dataSource.setLogWriter(out);
}
@Override
public int getLoginTimeout() throws SQLException {
return this.dataSource.getLoginTimeout();
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
this.dataSource.setLoginTimeout(seconds);
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return this.dataSource.unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return this.dataSource.isWrapperFor(iface);
}
// getParentLogger() is required for JDBC 4.1 compatibility
@Override
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
@Override
public void shutdown() {
shutdownDatabase();
}
}
}
六、策略模式
Spring框架的Resource资源接口实现方式.
查看接口类图.
在ApplicationContext中进行选择,使用 Resource 作为属性进行策略选择.
七、适配器模式
1、MyBatis的适配器接口,但是像Mybatis这种框架来说,它本身在运行的过程中也需要产生日志,但是Mybatis框架在设计的时候,无法知道项目中具体使用的是什么日志框架,所以只能适配各种日志框架,项目中使用什么框架,Mybatis就使用什么框架。
package org.apache.ibatis.logging;
/**
* @author Clinton Begin
*/
public interface Log {
boolean isDebugEnabled();
boolean isTraceEnabled();
void error(String s, Throwable e);
void error(String s);
void debug(String s);
void trace(String s);
void warn(String s);
}
而不同的日志框架,只需要适配这个接口就可以了
就拿Slf4j的实现来看,内部依赖了一个Slf4j框架中的Logger对象,最后所有日志的打印都是通过Slf4j框架中的Logger对象来实现的。
此外,Mybatis还提供了如下的一些实现,根据项目中的需要在配置文件中进行配置即可.
2、Alibaba的druid里面也是根据业务系统数据库需要不同的日志组件。
定义Log接口.
package com.alibaba.druid.support.logging;
public interface Log {
boolean isDebugEnabled();
void error(String msg, Throwable e);
void error(String msg);
boolean isInfoEnabled();
void info(String msg);
void debug(String msg);
void debug(String msg, Throwable e);
boolean isWarnEnabled();
void warn(String msg);
void warn(String msg, Throwable e);
boolean isErrorEnabled();
int getErrorCount();
int getWarnCount();
int getInfoCount();
int getDebugCount();
void resetStat();
}
Log4j2Impl
3、Dubbo的日志组件
package org.apache.dubbo.common.logger;
/**
* Logger interface
* <p>
* This interface is referred from commons-logging
*/
public interface Logger {
/**
* Logs a message with trace log level.
*
* @param msg log this message
*/
void trace(String msg);
/**
* Logs an error with trace log level.
*
* @param e log this cause
*/
void trace(Throwable e);
/**
* Logs an error with trace log level.
*
* @param msg log this message
* @param e log this cause
*/
void trace(String msg, Throwable e);
/**
* Logs a message with debug log level.
*
* @param msg log this message
*/
void debug(String msg);
/**
* Logs an error with debug log level.
*
* @param e log this cause
*/
void debug(Throwable e);
/**
* Logs an error with debug log level.
*
* @param msg log this message
* @param e log this cause
*/
void debug(String msg, Throwable e);
/**
* Logs a message with info log level.
*
* @param msg log this message
*/
void info(String msg);
/**
* Logs an error with info log level.
*
* @param e log this cause
*/
void info(Throwable e);
/**
* Logs an error with info log level.
*
* @param msg log this message
* @param e log this cause
*/
void info(String msg, Throwable e);
/**
* Logs a message with warn log level.
*
* @param msg log this message
*/
void warn(String msg);
/**
* Logs a message with warn log level.
*
* @param e log this message
*/
void warn(Throwable e);
/**
* Logs a message with warn log level.
*
* @param msg log this message
* @param e log this cause
*/
void warn(String msg, Throwable e);
/**
* Logs a message with error log level.
*
* @param msg log this message
*/
void error(String msg);
/**
* Logs an error with error log level.
*
* @param e log this cause
*/
void error(Throwable e);
/**
* Logs an error with error log level.
*
* @param msg log this message
* @param e log this cause
*/
void error(String msg, Throwable e);
/**
* Is trace logging currently enabled?
*
* @return true if trace is enabled
*/
boolean isTraceEnabled();
/**
* Is debug logging currently enabled?
*
* @return true if debug is enabled
*/
boolean isDebugEnabled();
/**
* Is info logging currently enabled?
*
* @return true if info is enabled
*/
boolean isInfoEnabled();
/**
* Is warn logging currently enabled?
*
* @return true if warn is enabled
*/
boolean isWarnEnabled();
/**
* Is error logging currently enabled?
*
* @return true if error is enabled
*/
boolean isErrorEnabled();
}
Slf4jLogger的日志适配组件.
LoggerAdapter
LoggerFactory
package org.apache.dubbo.common.logger.support;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.utils.NetUtils;
public class FailsafeLogger implements Logger {
private Logger logger;
private static boolean disabled = false;
public FailsafeLogger(Logger logger) {
this.logger = logger;
}
public static void setDisabled(boolean disabled) {
FailsafeLogger.disabled = disabled;
}
static boolean getDisabled() {
return disabled;
}
public Logger getLogger() {
return logger;
}
public void setLogger(Logger logger) {
this.logger = logger;
}
private String appendContextMessage(String msg) {
return " [DUBBO] " + msg + ", dubbo version: " + Version.getVersion() + ", current host: " + NetUtils.getLocalHost();
}
@Override
public void trace(String msg, Throwable e) {
if (disabled) {
return;
}
try {
logger.trace(appendContextMessage(msg), e);
} catch (Throwable t) {
}
}
@Override
public void trace(Throwable e) {
if (disabled) {
return;
}
try {
logger.trace(e);
} catch (Throwable t) {
}
}
@Override
public void trace(String msg) {
if (disabled) {
return;
}
try {
logger.trace(appendContextMessage(msg));
} catch (Throwable t) {
}
}
@Override
public void debug(String msg, Throwable e) {
if (disabled) {
return;
}
try {
logger.debug(appendContextMessage(msg), e);
} catch (Throwable t) {
}
}
@Override
public void debug(Throwable e) {
if (disabled) {
return;
}
try {
logger.debug(e);
} catch (Throwable t) {
}
}
@Override
public void debug(String msg) {
if (disabled) {
return;
}
try {
logger.debug(appendContextMessage(msg));
} catch (Throwable t) {
}
}
@Override
public void info(String msg, Throwable e) {
if (disabled) {
return;
}
try {
logger.info(appendContextMessage(msg), e);
} catch (Throwable t) {
}
}
@Override
public void info(String msg) {
if (disabled) {
return;
}
try {
logger.info(appendContextMessage(msg));
} catch (Throwable t) {
}
}
@Override
public void warn(String msg, Throwable e) {
if (disabled) {
return;
}
try {
logger.warn(appendContextMessage(msg), e);
} catch (Throwable t) {
}
}
@Override
public void warn(String msg) {
if (disabled) {
return;
}
try {
logger.warn(appendContextMessage(msg));
} catch (Throwable t) {
}
}
@Override
public void error(String msg, Throwable e) {
if (disabled) {
return;
}
try {
logger.error(appendContextMessage(msg), e);
} catch (Throwable t) {
}
}
@Override
public void error(String msg) {
if (disabled) {
return;
}
try {
logger.error(appendContextMessage(msg));
} catch (Throwable t) {
}
}
@Override
public void error(Throwable e) {
if (disabled) {
return;
}
try {
logger.error(e);
} catch (Throwable t) {
}
}
@Override
public void info(Throwable e) {
if (disabled) {
return;
}
try {
logger.info(e);
} catch (Throwable t) {
}
}
@Override
public void warn(Throwable e) {
if (disabled) {
return;
}
try {
logger.warn(e);
} catch (Throwable t) {
}
}
@Override
public boolean isTraceEnabled() {
if (disabled) {
return false;
}
try {
return logger.isTraceEnabled();
} catch (Throwable t) {
return false;
}
}
@Override
public boolean isDebugEnabled() {
if (disabled) {
return false;
}
try {
return logger.isDebugEnabled();
} catch (Throwable t) {
return false;
}
}
@Override
public boolean isInfoEnabled() {
if (disabled) {
return false;
}
try {
return logger.isInfoEnabled();
} catch (Throwable t) {
return false;
}
}
@Override
public boolean isWarnEnabled() {
if (disabled) {
return false;
}
try {
return logger.isWarnEnabled();
} catch (Throwable t) {
return false;
}
}
@Override
public boolean isErrorEnabled() {
if (disabled) {
return false;
}
try {
return logger.isErrorEnabled();
} catch (Throwable t) {
return false;
}
}
}